diff options
395 files changed, 9449 insertions, 10976 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 9e308435c9bc..de1a0d7e7fd6 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -71,6 +71,7 @@ aconfig_srcjars = [ ":android.appwidget.flags-aconfig-java{.generated_srcjars}", ":android.webkit.flags-aconfig-java{.generated_srcjars}", ":android.provider.flags-aconfig-java{.generated_srcjars}", + ":android.chre.flags-aconfig-java{.generated_srcjars}", ] filegroup { @@ -905,3 +906,10 @@ java_aconfig_library { aconfig_declarations: "android.provider.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// ContextHub +java_aconfig_library { + name: "android.chre.flags-aconfig-java", + aconfig_declarations: "chre_flags", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Android.bp b/Android.bp index f3b2ebb4fc17..c84de511719a 100644 --- a/Android.bp +++ b/Android.bp @@ -175,9 +175,6 @@ java_library { // and remove this line. "//frameworks/base/tools/hoststubgen:__subpackages__", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // AIDL files under these paths are mixture of public and private ones. @@ -270,9 +267,6 @@ java_library { ], sdk_version: "core_platform", installable: false, - lint: { - baseline_filename: "lint-baseline.xml", - }, } // NOTE: This filegroup is exposed for vendor libraries to depend on and is referenced in @@ -441,9 +435,6 @@ java_library { ], sdk_version: "core_platform", installable: false, - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Separated so framework-minus-apex-defaults can be used without the libs dependency @@ -487,9 +478,6 @@ java_library { ], compile_dex: false, headers_only: true, - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -534,7 +522,7 @@ java_library { }, lint: { enabled: false, - baseline_filename: "lint-baseline.xml", + }, } @@ -559,9 +547,6 @@ java_library { ], sdk_version: "core_platform", apex_available: ["//apex_available:platform"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -577,9 +562,6 @@ java_library { "calendar-provider-compat-config", "contacts-provider-platform-compat-config", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } platform_compat_config { @@ -634,9 +616,6 @@ java_library { "rappor", ], dxflags: ["--core-library"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // utility classes statically linked into framework-wifi and dynamically linked 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 d940e380fa7e..b0f378d1752d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -159,6 +159,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -513,6 +514,10 @@ public class JobSchedulerService extends com.android.server.SystemService if (name == null) { continue; } + if (DEBUG) { + Slog.d(TAG, "DeviceConfig " + name + + " changed to " + properties.getString(name, null)); + } switch (name) { case Constants.KEY_ENABLE_API_QUOTAS: case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: @@ -3507,7 +3512,10 @@ public class JobSchedulerService extends com.android.server.SystemService } final boolean shouldForceBatchJob; - if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { + if (job.overrideState > JobStatus.OVERRIDE_NONE) { + // The job should run for some test. Don't force batch it. + shouldForceBatchJob = false; + } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { // Never batch expedited or user-initiated jobs, even for RESTRICTED apps. shouldForceBatchJob = false; } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { @@ -4960,6 +4968,8 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId + " " + jobId + " s=" + satisfied + " f=" + force); + final CountDownLatch delayLatch = new CountDownLatch(1); + final JobStatus js; try { final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); @@ -4968,7 +4978,7 @@ public class JobSchedulerService extends com.android.server.SystemService } synchronized (mLock) { - final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); + js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); if (js == null) { return JobSchedulerShellCommand.CMD_ERR_NO_JOB; } @@ -4979,23 +4989,71 @@ public class JobSchedulerService extends com.android.server.SystemService // Re-evaluate constraints after the override is set in case one of the overridden // constraints was preventing another constraint from thinking it needed to update. for (int c = mControllers.size() - 1; c >= 0; --c) { - mControllers.get(c).reevaluateStateLocked(uid); + mControllers.get(c).evaluateStateLocked(js); } if (!js.isConstraintsSatisfied()) { - js.overrideState = JobStatus.OVERRIDE_NONE; - return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + if (js.hasConnectivityConstraint() + && !js.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY) + && js.wouldBeReadyWithConstraint(JobStatus.CONSTRAINT_CONNECTIVITY)) { + // Because of how asynchronous the connectivity signals are, JobScheduler + // may not get the connectivity satisfaction signal immediately. In this + // case, wait a few seconds to see if it comes in before saying the + // connectivity constraint isn't satisfied. + mHandler.postDelayed( + checkConstraintRunnableForTesting( + mHandler, js, delayLatch, 5, 1000), + 1000); + } else { + // There's no asynchronous signal to wait for. We can immediately say the + // job's constraints aren't satisfied and return. + js.overrideState = JobStatus.OVERRIDE_NONE; + return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + } + } else { + delayLatch.countDown(); } - - queueReadyJobsForExecutionLocked(); - maybeRunPendingJobsLocked(); } } catch (RemoteException e) { // can't happen + return 0; + } + + // Choose to block the return until we're sure about the state of the connectivity job + // so that tests can expect a reliable state after calling the run command. + try { + delayLatch.await(7L, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Slog.e(TAG, "Couldn't wait for asynchronous constraint change", e); + } + + synchronized (mLock) { + if (!js.isConstraintsSatisfied()) { + js.overrideState = JobStatus.OVERRIDE_NONE; + return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; + } + + queueReadyJobsForExecutionLocked(); + maybeRunPendingJobsLocked(); } return 0; } + private static Runnable checkConstraintRunnableForTesting(@NonNull final Handler handler, + @NonNull final JobStatus js, @NonNull final CountDownLatch latch, + final int remainingAttempts, final long delayMs) { + return () -> { + if (remainingAttempts <= 0 || js.isConstraintsSatisfied()) { + latch.countDown(); + return; + } + handler.postDelayed( + checkConstraintRunnableForTesting( + handler, js, latch, remainingAttempts - 1, delayMs), + delayMs); + }; + } + // Shell command infrastructure: immediately timeout currently executing jobs int executeStopCommand(PrintWriter pw, String pkgName, int userId, @Nullable String namespace, boolean hasJobId, int jobId, diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index bdc2246475d6..d39863c85f33 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -2192,7 +2192,7 @@ public final class JobStatus { * @return Whether or not this job would be ready to run if it had the specified constraint * granted, based on its requirements. */ - boolean wouldBeReadyWithConstraint(int constraint) { + public boolean wouldBeReadyWithConstraint(int constraint) { return readinessStatusWithConstraint(constraint, true); } diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp index bcfb68ffd04d..6b8c02f901f1 100644 --- a/api/ApiDocs.bp +++ b/api/ApiDocs.bp @@ -183,6 +183,7 @@ doc_defaults { "-federationapi AndroidX $(location :current-androidx-api)", // doclava contains checks for a few issues that are have been migrated to metalava. // disable them in doclava, to avoid mistriggering or double triggering. + "-hide 101", // TODO: turn Lint 101 back into an error again "-hide 111", // HIDDEN_SUPERCLASS "-hide 113", // DEPRECATION_MISMATCH "-hide 125", // REQUIRES_PERMISSION diff --git a/api/api.go b/api/api.go index 2668999c572e..43713aad0e1e 100644 --- a/api/api.go +++ b/api/api.go @@ -64,6 +64,7 @@ type CombinedApisProperties struct { type CombinedApis struct { android.ModuleBase + android.DefaultableModuleBase properties CombinedApisProperties } @@ -74,6 +75,7 @@ func init() { func registerBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory) + ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory) } var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents) @@ -409,6 +411,7 @@ func combinedApisModuleFactory() android.Module { module := &CombinedApis{} module.AddProperties(&module.properties) android.InitAndroidModule(module) + android.InitDefaultableModule(module) android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) return module } @@ -445,3 +448,16 @@ func remove(s []string, v string) []string { } return s2 } + +// Defaults +type CombinedApisModuleDefaults struct { + android.ModuleBase + android.DefaultsModuleBase +} + +func CombinedApisModuleDefaultsFactory() android.Module { + module := &CombinedApisModuleDefaults{} + module.AddProperties(&CombinedApisProperties{}) + android.InitDefaultsModule(module) + return module +} diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md index e7361fe95e8e..b6e4e0de8cf7 100644 --- a/cmds/uinput/README.md +++ b/cmds/uinput/README.md @@ -7,11 +7,23 @@ There are two ways to use the `uinput` command: or app (such as the CTS tests via [`UinputDevice`][UinputDevice]). * `uinput <filename>` reads commands from a file instead of standard input. +There are also two supported input formats, described in the sections below. The tool will +automatically detect which format is being used. + [UinputDevice]: https://cs.android.com/android/platform/superproject/main/+/main:cts/libs/input/src/com/android/cts/input/UinputDevice.java -## Command format +## evemu recording format (recommended) + +`uinput` supports the evemu format, as used by the [FreeDesktop project's evemu suite][FreeDesktop]. +This is a simple text-based format compatible with recording and replay tools on other platforms. +However, it only supports playback of events from one device from a single recording. Recordings can +be made using the `evemu-record` command on Android or other Linux-based OSes. + +[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu + +## JSON-like format -Input commands should be in JSON format, though the parser is in [lenient mode] to allow comments, +The other supported format is JSON-based, though the parser is in [lenient mode] to allow comments, and integers can be specified in hexadecimal (e.g. `0xABCD`). The input file (or standard input) can contain multiple commands, which will be executed in sequence. Simply add multiple JSON objects to the file, one after the other without separators: @@ -34,9 +46,9 @@ Many examples of command files can be found [in the CTS tests][cts-example-jsons [lenient mode]: https://developer.android.com/reference/android/util/JsonReader#setLenient(boolean) [cts-example-jsons]: https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/tests/hardware/res/raw/ -## Command reference +### Command reference -### `register` +#### `register` Register a new uinput device @@ -122,7 +134,7 @@ Example: [struct input_absinfo]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/kernel/uapi/linux/input.h?q=%22struct%20input_absinfo%22 -#### Waiting for registration +##### Waiting for registration After the command is sent, there will be a delay before the device is set up by the Android input stack, and `uinput` does not wait for that process to finish. Any commands sent to the device during @@ -135,12 +147,12 @@ finished processing. [onInputDeviceAdded]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html -#### Unregistering the device +##### Unregistering the device As soon as EOF is reached (either in interactive mode, or in file mode), the device that was created will be unregistered. There is no explicit command for unregistering a device. -### `delay` +#### `delay` Add a delay to command processing @@ -160,7 +172,7 @@ Example: } ``` -### `inject` +#### `inject` Send an array of uinput event packets to the uinput device @@ -190,7 +202,7 @@ keys would look like this: } ``` -### `sync` +#### `sync` A command used to get a response once the command is processed. When several `inject` and `delay` commands are used in a row, the `sync` command can be used to track the progress of the command diff --git a/core/api/current.txt b/core/api/current.txt index 6469e893e6be..8b73243540ee 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -49,6 +49,7 @@ package android { field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; field public static final String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; + field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String BIND_TV_AD_SERVICE = "android.permission.BIND_TV_AD_SERVICE"; field public static final String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; field public static final String BIND_TV_INTERACTIVE_APP = "android.permission.BIND_TV_INTERACTIVE_APP"; field public static final String BIND_VISUAL_VOICEMAIL_SERVICE = "android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"; @@ -4370,7 +4371,7 @@ package android.app { method public final android.media.session.MediaController getMediaController(); method @NonNull public android.view.MenuInflater getMenuInflater(); method @NonNull public android.window.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); - method @Deprecated public final android.app.Activity getParent(); + method public final android.app.Activity getParent(); method @Nullable public android.content.Intent getParentActivityIntent(); method public android.content.SharedPreferences getPreferences(int); method @Nullable public android.net.Uri getReferrer(); @@ -4388,7 +4389,7 @@ package android.app { method public void invalidateOptionsMenu(); method public boolean isActivityTransitionRunning(); method public boolean isChangingConfigurations(); - method @Deprecated public final boolean isChild(); + method public final boolean isChild(); method public boolean isDestroyed(); method public boolean isFinishing(); method public boolean isImmersive(); @@ -19733,7 +19734,7 @@ package android.hardware.camera2.params { @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class LensIntrinsicsSample { ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public LensIntrinsicsSample(long, @NonNull float[]); method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public float[] getLensIntrinsics(); - method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestamp(); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestampNanos(); } public final class LensShadingMap { @@ -33400,7 +33401,7 @@ package android.os { @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitorReadings { method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getConsumedEnergy(@NonNull android.os.PowerMonitor); - method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestamp(@NonNull android.os.PowerMonitor); + method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestampMillis(@NonNull android.os.PowerMonitor); field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int ENERGY_UNAVAILABLE = -1; // 0xffffffff } @@ -33410,6 +33411,7 @@ package android.os { method public static final long getElapsedCpuTime(); method public static final int[] getExclusiveCores(); method public static final int getGidForName(String); + method @FlaggedApi("com.android.sdksandbox.flags.sdk_sandbox_uid_to_app_uid_api") public static final int getSdkSandboxUidForAppUid(int); method public static long getStartElapsedRealtime(); method public static long getStartRequestedElapsedRealtime(); method public static long getStartRequestedUptimeMillis(); @@ -33994,8 +33996,8 @@ package android.os.health { } public class SystemHealthManager { - method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable android.os.Handler, @NonNull java.util.function.Consumer<android.os.PowerMonitorReadings>, @NonNull java.util.function.Consumer<java.lang.RuntimeException>); - method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable android.os.Handler, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>); + method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>); + method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>); method public android.os.health.HealthStats takeMyUidSnapshot(); method public android.os.health.HealthStats takeUidSnapshot(int); method public android.os.health.HealthStats[] takeUidSnapshots(int[]); @@ -45341,7 +45343,7 @@ package android.telephony { method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid); method public boolean canManageSubscription(android.telephony.SubscriptionInfo); - method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); + method @FlaggedApi("com.android.internal.telephony.flags.enforce_subscription_user_filter") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context); method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); @@ -47293,6 +47295,7 @@ package android.text { method public int getLineForOffset(int); method public int getLineForVertical(int); method public float getLineLeft(int); + method @FlaggedApi("com.android.text.flags.inter_character_justification") @IntRange(from=0) public int getLineLetterSpacingUnitCount(@IntRange(from=0) int, boolean); method public float getLineMax(int); method public float getLineRight(int); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount(); @@ -56498,6 +56501,7 @@ package android.view.textclassifier { field public static final String TYPE_EMAIL = "email"; field public static final String TYPE_FLIGHT_NUMBER = "flight"; field public static final String TYPE_OTHER = "other"; + field @FlaggedApi("android.service.notification.redact_sensitive_notifications_from_untrusted_listeners") public static final String TYPE_OTP_CODE = "otp_code"; field public static final String TYPE_PHONE = "phone"; field public static final String TYPE_UNKNOWN = ""; field public static final String TYPE_URL = "url"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2898705b75d4..a532cdbe4d7a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1596,12 +1596,14 @@ package android.app.ambientcontext { method public int getDensityLevel(); method @NonNull public java.time.Instant getEndTime(); method public int getEventType(); + method @FlaggedApi("android.app.ambient_heart_rate") @IntRange(from=0xffffffff) public int getRatePerMinute(); method @NonNull public java.time.Instant getStartTime(); method @NonNull public android.os.PersistableBundle getVendorData(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR; field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3 field public static final int EVENT_COUGH = 1; // 0x1 + field @FlaggedApi("android.app.ambient_heart_rate") public static final int EVENT_HEART_RATE = 4; // 0x4 field public static final int EVENT_SNORE = 2; // 0x2 field public static final int EVENT_UNKNOWN = 0; // 0x0 field public static final int EVENT_VENDOR_WEARABLE_START = 100000; // 0x186a0 @@ -1621,6 +1623,7 @@ package android.app.ambientcontext { method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int); + method @FlaggedApi("android.app.ambient_heart_rate") @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setRatePerMinute(@IntRange(from=0xffffffff) int); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant); method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setVendorData(@NonNull android.os.PersistableBundle); } @@ -14681,6 +14684,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled(); + method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNullCipherNotificationsEnabled(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); @@ -14720,6 +14724,7 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean); method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean); + method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableNullCipherNotifications(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean); @@ -15127,7 +15132,7 @@ package android.telephony.data { method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>); method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile); method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback); - method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback); @@ -16334,7 +16339,7 @@ package android.telephony.ims { public interface RegistrationManager { field public static final int SUGGESTED_ACTION_NONE = 0; // 0x0 - field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4; // 0x4 + field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4; // 0x4 field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK = 1; // 0x1 field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2; // 0x2 field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_RAT_BLOCK = 3; // 0x3 @@ -17032,8 +17037,8 @@ package android.telephony.satellite { @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class PointingInfo implements android.os.Parcelable { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents(); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteAzimuthDegrees(); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteElevationDegrees(); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffff4c, to=180) public float getSatelliteAzimuthDegrees(); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffffa6, to=90) public float getSatelliteElevationDegrees(); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int); field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR; } @@ -17072,7 +17077,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback) throws android.telephony.satellite.SatelliteManager.SatelliteException; method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); @@ -17093,7 +17098,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback); field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1 @@ -17163,12 +17168,12 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getErrorCode(); } - @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback { - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean); + @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteModemStateCallback { + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int); } - @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteStateCallback { - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int); + @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback { + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean); } @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteTransmissionUpdateCallback { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index b2a28b2127bc..0505af4488ff 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -2359,8 +2359,8 @@ UnflaggedApi: android.telephony.satellite.SatelliteManager#provisionSatelliteSer New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.provisionSatelliteService(String,byte[],android.os.CancellationSignal,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>) UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteDatagram(java.util.concurrent.Executor, android.telephony.satellite.SatelliteDatagramCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteDatagram(java.util.concurrent.Executor,android.telephony.satellite.SatelliteDatagramCallback) -UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteStateCallback): - New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteStateCallback) +UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteModemStateCallback): + New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteModemStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteProvisionStateCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteProvisionStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager#requestIsDemoModeEnabled(java.util.concurrent.Executor, android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>): @@ -2389,8 +2389,8 @@ UnflaggedApi: android.telephony.satellite.SatelliteManager#stopSatelliteTransmis New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>) UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback) -UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback): - New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback) +UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback): + New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback) UnflaggedApi: android.telephony.satellite.SatelliteManager.SatelliteException: @@ -2403,10 +2403,10 @@ UnflaggedApi: android.telephony.satellite.SatelliteProvisionStateCallback: New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteProvisionStateCallback UnflaggedApi: android.telephony.satellite.SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean): New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteProvisionStateCallback.onSatelliteProvisionStateChanged(boolean) -UnflaggedApi: android.telephony.satellite.SatelliteStateCallback: - New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteStateCallback -UnflaggedApi: android.telephony.satellite.SatelliteStateCallback#onSatelliteModemStateChanged(int): - New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteStateCallback.onSatelliteModemStateChanged(int) +UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback: + New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteModemStateCallback +UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback#onSatelliteModemStateChanged(int): + New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteModemStateCallback.onSatelliteModemStateChanged(int) UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback: New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteTransmissionUpdateCallback UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback#onReceiveDatagramStateChanged(int, int, int): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2e22071d72ad..bbe03a3d11a2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3735,6 +3735,7 @@ package android.view.animation { public class AnimationUtils { method @FlaggedApi("android.view.flags.expected_presentation_time_read_only") public static void lockAnimationClock(long, long); + method public static void lockAnimationClock(long); method public static void unlockAnimationClock(); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f9583d22cf58..2103055afe50 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1177,23 +1177,12 @@ public class Activity extends ContextThemeWrapper return mApplication; } - /** - * Whether this is a child {@link Activity} of an {@link ActivityGroup}. - * - * @deprecated {@link ActivityGroup} is deprecated. - */ - @Deprecated + /** Is this activity embedded inside of another activity? */ public final boolean isChild() { return mParent != null; } - /** - * Returns the parent {@link Activity} if this is a child {@link Activity} of an - * {@link ActivityGroup}. - * - * @deprecated {@link ActivityGroup} is deprecated. - */ - @Deprecated + /** Return the parent activity if this view is an embedded child. */ public final Activity getParent() { return mParent; } @@ -3075,7 +3064,7 @@ public class Activity extends ContextThemeWrapper } /** - * Request to put the freeform activity into fullscreen. The requester has to be the top-most + * Request to put the activity into fullscreen. The requester must be pinned or the top-most * activity of the focused display which can be verified using * {@link #onTopResumedActivityChanged(boolean)}. The request should also be a response to a * user input. When getting fullscreen and receiving corresponding diff --git a/core/java/android/app/ambient_context.aconfig b/core/java/android/app/ambient_context.aconfig new file mode 100644 index 000000000000..3f73da216b9f --- /dev/null +++ b/core/java/android/app/ambient_context.aconfig @@ -0,0 +1,8 @@ +package: "android.app" + +flag { + namespace: "biometrics_integration" + name: "ambient_heart_rate" + description: "Feature flag for adding heart rate api to ambient context." + bug: "318309481" +} diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java index b5c66ffa72a1..f94987e8495a 100644 --- a/core/java/android/app/ambientcontext/AmbientContextEvent.java +++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java @@ -16,7 +16,9 @@ package android.app.ambientcontext; +import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcelable; @@ -68,6 +70,14 @@ public final class AmbientContextEvent implements Parcelable { public static final int EVENT_BACK_DOUBLE_TAP = 3; /** + * The integer indicating a heart rate measurement was done. + * + * @see #getRatePerMinute + */ + @Event @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) + public static final int EVENT_HEART_RATE = 4; + + /** * Integer indicating the start of wearable vendor defined events that can be detected. * These depend on the vendor implementation. */ @@ -79,12 +89,16 @@ public final class AmbientContextEvent implements Parcelable { */ public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name"; + /** Default value for the rate per minute data field. */ + private static final int RATE_PER_MINUTE_UNKNOWN = -1; + /** @hide */ @IntDef(prefix = { "EVENT_" }, value = { EVENT_UNKNOWN, EVENT_COUGH, EVENT_SNORE, EVENT_BACK_DOUBLE_TAP, + EVENT_HEART_RATE, EVENT_VENDOR_WEARABLE_START, }) @Retention(RetentionPolicy.SOURCE) @@ -170,6 +184,16 @@ public final class AmbientContextEvent implements Parcelable { return new PersistableBundle(); } + /** + * Rate per minute of the event during the start to end time. + * + * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown. + */ + private final @IntRange(from = -1) int mRatePerMinute; + private static int defaultRatePerMinute() { + return RATE_PER_MINUTE_UNKNOWN; + } + // Code below generated by codegen v1.0.23. @@ -179,6 +203,8 @@ public final class AmbientContextEvent implements Parcelable { // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java + // then manually add @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) back to flagged + // APIs. // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -191,6 +217,7 @@ public final class AmbientContextEvent implements Parcelable { EVENT_COUGH, EVENT_SNORE, EVENT_BACK_DOUBLE_TAP, + EVENT_HEART_RATE, EVENT_VENDOR_WEARABLE_START }) @Retention(RetentionPolicy.SOURCE) @@ -209,6 +236,8 @@ public final class AmbientContextEvent implements Parcelable { return "EVENT_SNORE"; case EVENT_BACK_DOUBLE_TAP: return "EVENT_BACK_DOUBLE_TAP"; + case EVENT_HEART_RATE: + return "EVENT_HEART_RATE"; case EVENT_VENDOR_WEARABLE_START: return "EVENT_VENDOR_WEARABLE_START"; default: return Integer.toHexString(value); @@ -255,7 +284,8 @@ public final class AmbientContextEvent implements Parcelable { @NonNull Instant endTime, @LevelValue int confidenceLevel, @LevelValue int densityLevel, - @NonNull PersistableBundle vendorData) { + @NonNull PersistableBundle vendorData, + @IntRange(from = -1) int ratePerMinute) { this.mEventType = eventType; com.android.internal.util.AnnotationValidations.validate( EventCode.class, null, mEventType); @@ -274,6 +304,10 @@ public final class AmbientContextEvent implements Parcelable { this.mVendorData = vendorData; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mVendorData); + this.mRatePerMinute = ratePerMinute; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mRatePerMinute, + "from", -1); // onConstructed(); // You can define this method to get a callback } @@ -330,6 +364,17 @@ public final class AmbientContextEvent implements Parcelable { return mVendorData; } + /** + * Rate per minute of the event during the start to end time. + * + * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown. + */ + @DataClass.Generated.Member + @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) + public @IntRange(from = -1) int getRatePerMinute() { + return mRatePerMinute; + } + @Override @DataClass.Generated.Member public String toString() { @@ -342,7 +387,8 @@ public final class AmbientContextEvent implements Parcelable { "endTime = " + mEndTime + ", " + "confidenceLevel = " + mConfidenceLevel + ", " + "densityLevel = " + mDensityLevel + ", " + - "vendorData = " + mVendorData + + "vendorData = " + mVendorData + ", " + + "ratePerMinute = " + mRatePerMinute + " }"; } @@ -380,6 +426,7 @@ public final class AmbientContextEvent implements Parcelable { dest.writeInt(mConfidenceLevel); dest.writeInt(mDensityLevel); dest.writeTypedObject(mVendorData, flags); + dest.writeInt(mRatePerMinute); } @Override @@ -399,6 +446,7 @@ public final class AmbientContextEvent implements Parcelable { int confidenceLevel = in.readInt(); int densityLevel = in.readInt(); PersistableBundle vendorData = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR); + int ratePerMinute = in.readInt(); this.mEventType = eventType; com.android.internal.util.AnnotationValidations.validate( @@ -418,6 +466,10 @@ public final class AmbientContextEvent implements Parcelable { this.mVendorData = vendorData; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mVendorData); + this.mRatePerMinute = ratePerMinute; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mRatePerMinute, + "from", -1); // onConstructed(); // You can define this method to get a callback } @@ -449,6 +501,7 @@ public final class AmbientContextEvent implements Parcelable { private @LevelValue int mConfidenceLevel; private @LevelValue int mDensityLevel; private @NonNull PersistableBundle mVendorData; + private @IntRange(from = -1) int mRatePerMinute; private long mBuilderFieldsSet = 0L; @@ -525,10 +578,22 @@ public final class AmbientContextEvent implements Parcelable { return this; } + /** + * Rate per minute of the event during the start to end time. + */ + @DataClass.Generated.Member + @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) + public @NonNull Builder setRatePerMinute(@IntRange(from = -1) int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mRatePerMinute = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull AmbientContextEvent build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mEventType = defaultEventType(); @@ -548,18 +613,22 @@ public final class AmbientContextEvent implements Parcelable { if ((mBuilderFieldsSet & 0x20) == 0) { mVendorData = defaultVendorData(); } + if ((mBuilderFieldsSet & 0x40) == 0) { + mRatePerMinute = defaultRatePerMinute(); + } AmbientContextEvent o = new AmbientContextEvent( mEventType, mStartTime, mEndTime, mConfidenceLevel, mDensityLevel, - mVendorData); + mVendorData, + mRatePerMinute); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -567,10 +636,10 @@ public final class AmbientContextEvent implements Parcelable { } @DataClass.Generated( - time = 1671217108067L, + time = 1704895515931L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java", - inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final @android.app.ambientcontext.AmbientContextEvent.Event @android.annotation.FlaggedApi int EVENT_HEART_RATE\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\nprivate static final int RATE_PER_MINUTE_UNKNOWN\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate final @android.annotation.IntRange int mRatePerMinute\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nprivate static int defaultRatePerMinute()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 637187120922..a41cb1fa6ea8 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -239,7 +239,7 @@ public class FullBackup { Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e); } - byte[] buffer = new byte[32 * 1024]; + byte[] buffer = new byte[64 * 1024]; final long origSize = size; FileInputStream in = new FileInputStream(data.getFileDescriptor()); while (size > 0) { diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS index 073e2d79850b..497eaf0e40f1 100644 --- a/core/java/android/app/wearable/OWNERS +++ b/core/java/android/app/wearable/OWNERS @@ -1,3 +1,5 @@ charliewang@google.com +hackz@google.com oni@google.com +tomchan@google.com volnov@google.com
\ No newline at end of file diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS index 90c3d04d62d0..a37408b7d847 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -4,6 +4,7 @@ per-file ContextWrapper.java = * per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS +per-file UriRelativeFilter* = file:/PACKAGE_MANAGER_OWNERS per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS per-file Intent.java = file:/INTENT_OWNERS per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 3affb73d1075..0cd1c8ca89fe 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -79,6 +79,8 @@ import android.util.Log; import android.util.Range; import android.util.Size; +import com.android.internal.camera.flags.Flags; + import dalvik.annotation.optimization.FastNative; import dalvik.system.VMRuntime; @@ -1795,49 +1797,57 @@ public class CameraMetadataNative implements Parcelable { return false; } - long[] tsArray = new long[samples.length]; - float[] intrinsicsArray = new float[samples.length * 5]; - for (int i = 0; i < samples.length; i++) { - tsArray[i] = samples[i].getTimestamp(); - System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5*i, 5); + if (Flags.concertMode()) { + long[] tsArray = new long[samples.length]; + float[] intrinsicsArray = new float[samples.length * 5]; + for (int i = 0; i < samples.length; i++) { + tsArray[i] = samples[i].getTimestampNanos(); + System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5 * i, 5); - } - setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray); - setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray); + } + setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray); + setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray); - return true; + return true; + } else { + return false; + } } private LensIntrinsicsSample[] getLensIntrinsicSamples() { - long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS); - float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES); + if (Flags.concertMode()) { + long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS); + float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES); - if (timestamps == null) { - if (intrinsics != null) { - throw new AssertionError("timestamps is null but intrinsics is not"); - } + if (timestamps == null) { + if (intrinsics != null) { + throw new AssertionError("timestamps is null but intrinsics is not"); + } - return null; - } + return null; + } - if (intrinsics == null) { - throw new AssertionError("timestamps is not null but intrinsics is"); - } else if((intrinsics.length % 5) != 0) { - throw new AssertionError("intrinsics are not multiple of 5"); - } + if (intrinsics == null) { + throw new AssertionError("timestamps is not null but intrinsics is"); + } else if ((intrinsics.length % 5) != 0) { + throw new AssertionError("intrinsics are not multiple of 5"); + } - if ((intrinsics.length / 5) != timestamps.length) { - throw new AssertionError(String.format( - "timestamps has %d entries but intrinsics has %d", timestamps.length, - intrinsics.length / 5)); - } + if ((intrinsics.length / 5) != timestamps.length) { + throw new AssertionError(String.format( + "timestamps has %d entries but intrinsics has %d", timestamps.length, + intrinsics.length / 5)); + } - LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length]; - for (int i = 0; i < timestamps.length; i++) { - float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5*i, 5*i + 5); - samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic); + LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length]; + for (int i = 0; i < timestamps.length; i++) { + float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5 * i, 5 * i + 5); + samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic); + } + return samples; + } else { + return null; } - return samples; } private Capability[] getExtendedSceneModeCapabilities() { diff --git a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java index 575cbfae34e3..9a4ec5c94b5b 100644 --- a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java +++ b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java @@ -37,16 +37,18 @@ public final class LensIntrinsicsSample { * Create a new {@link LensIntrinsicsSample}. * * <p>{@link LensIntrinsicsSample} contains the timestamp and the - * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample. + * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample.</p> * - * @param timestamp timestamp of the lens intrinsics sample. - * @param lensIntrinsics the lens intrinsic calibration for the sample. + * @param timestampNs timestamp in nanoseconds of the lens intrinsics sample. This uses the + * same time basis as {@link CaptureResult#SENSOR_TIMESTAMP}. + * @param lensIntrinsics the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsic} + * calibration for the sample. * * @throws IllegalArgumentException if lensIntrinsics length is different from 5 */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public LensIntrinsicsSample(final long timestamp, @NonNull final float[] lensIntrinsics) { - mTimestampNs = timestamp; + public LensIntrinsicsSample(final long timestampNs, @NonNull final float[] lensIntrinsics) { + mTimestampNs = timestampNs; Preconditions.checkArgument(lensIntrinsics.length == 5); mLensIntrinsics = lensIntrinsics; } @@ -54,18 +56,18 @@ public final class LensIntrinsicsSample { /** * Get the timestamp in nanoseconds. * - *<p>The timestamps are in the same timebase as and comparable to + *<p>The timestamps are in the same time basis as and comparable to *{@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p> * * @return a long value (guaranteed to be finite) */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) - public long getTimestamp() { + public long getTimestampNanos() { return mTimestampNs; } /** - * Get the lens intrinsics calibration + * Get the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsics} calibration * * @return a floating point value (guaranteed to be finite) * @see CaptureResult#LENS_INTRINSIC_CALIBRATION diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 134a510df5f3..8f0e0c911f56 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -1831,15 +1831,6 @@ public final class DisplayManager { String KEY_POWER_THROTTLING_DATA = "power_throttling_data"; /** - * Key for new power controller feature flag. If enabled new DisplayPowerController will - * be used. - * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)} - * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace. - * @hide - */ - String KEY_NEW_POWER_CONTROLLER = "use_newly_structured_display_power_controller"; - - /** * Key for normal brightness mode controller feature flag. * It enables NormalBrightnessModeController. * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)} diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 70cf97308ffb..561db9c8a8ce 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -102,6 +102,24 @@ public class VcnManager { public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY = "vcn_network_selection_wifi_exit_rssi_threshold"; + /** + * Key for the interval to poll IpSecTransformState for packet loss monitoring + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY = + "vcn_network_selection_poll_ipsec_state_interval_seconds"; + + /** + * Key for the threshold of IPSec packet loss rate + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY = + "vcn_network_selection_ipsec_packet_loss_percent_threshold"; + // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz /** @@ -115,6 +133,20 @@ public class VcnManager { "vcn_restricted_transports"; /** + * Key for number of seconds to wait before entering safe mode + * + * <p>A VcnGatewayConnection will enter safe mode when it takes over the configured timeout to + * enter {@link ConnectedState}. + * + * <p>Defaults to 30, unless overridden by carrier config + * + * @hide + */ + @NonNull + public static final String VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY = + "vcn_safe_mode_timeout_seconds_key"; + + /** * Key for maximum number of parallel SAs for tunnel aggregation * * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be @@ -134,7 +166,10 @@ public class VcnManager { new String[] { VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, }; diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index 6956916af0f1..7afd72195fcb 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -5,4 +5,18 @@ flag { namespace: "vcn" description: "Feature flag for safe mode configurability" bug: "276358140" +} + +flag { + name: "safe_mode_timeout_config" + namespace: "vcn" + description: "Feature flag for adjustable safe mode timeout" + bug: "317406085" +} + +flag{ + name: "network_metric_monitor" + namespace: "vcn" + description: "Feature flag for enabling network metric monitor" + bug: "282996138" }
\ No newline at end of file diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index c60f949da408..fbec518e4a29 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -57,6 +57,7 @@ public final class MessageQueue { @UnsupportedAppUsage Message mMessages; + private Message mLast; @UnsupportedAppUsage private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; @@ -66,6 +67,10 @@ public final class MessageQueue { // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; + // Tracks the number of async message. We use this in enqueueMessage() to avoid searching the + // queue for async messages when inserting a message at the tail. + private int mAsyncMessageCount; + // The next barrier token. // Barriers are indicated by messages with a null target whose arg1 field carries the token. @UnsupportedAppUsage @@ -364,12 +369,21 @@ public final class MessageQueue { mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; + if (prevMsg.next == null) { + mLast = prevMsg; + } } else { mMessages = msg.next; + if (msg.next == null) { + mLast = null; + } } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); + if (msg.isAsynchronous()) { + mAsyncMessageCount--; + } return msg; } } else { @@ -492,6 +506,14 @@ public final class MessageQueue { msg.when = when; msg.arg1 = token; + if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) { + /* Message goes to tail of list */ + mLast.next = msg; + mLast = msg; + msg.next = null; + return token; + } + Message prev = null; Message p = mMessages; if (when != 0) { @@ -500,6 +522,12 @@ public final class MessageQueue { p = p.next; } } + + if (p == null) { + /* We reached the tail of the list, or list is empty. */ + mLast = msg; + } + if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; @@ -540,9 +568,15 @@ public final class MessageQueue { final boolean needWake; if (prev != null) { prev.next = p.next; + if (prev.next == null) { + mLast = prev; + } needWake = false; } else { mMessages = p.next; + if (mMessages == null) { + mLast = null; + } needWake = mMessages == null || mMessages.target != null; } p.recycleUnchecked(); @@ -582,24 +616,77 @@ public final class MessageQueue { msg.next = p; mMessages = msg; needWake = mBlocked; + if (p == null) { + mLast = mMessages; + } } else { - // Inserted within the middle of the queue. Usually we don't have to wake - // up the event queue unless there is a barrier at the head of the queue - // and the message is the earliest asynchronous message in the queue. - needWake = mBlocked && p.target == null && msg.isAsynchronous(); - Message prev; - for (;;) { - prev = p; - p = p.next; - if (p == null || when < p.when) { - break; + // Message is to be inserted at tail or middle of queue. Usually we don't have to + // wake up the event queue unless there is a barrier at the head of the queue and + // the message is the earliest asynchronous message in the queue. + // + // For readability, we split this portion of the function into two blocks based on + // whether tail tracking is enabled. This has a minor implication for the case + // where tail tracking is disabled. See the comment below. + if (Flags.messageQueueTailTracking()) { + needWake = mBlocked && p.target == null && msg.isAsynchronous() + && mAsyncMessageCount == 0; + if (when >= mLast.when) { + msg.next = null; + mLast.next = msg; + mLast = msg; + } else { + // Inserted within the middle of the queue. + Message prev; + for (;;) { + prev = p; + p = p.next; + if (p == null || when < p.when) { + break; + } + } + if (p == null) { + /* Inserting at tail of queue */ + mLast = msg; + } + msg.next = p; // invariant: p == prev.next + prev.next = msg; } - if (needWake && p.isAsynchronous()) { - needWake = false; + } else { + needWake = mBlocked && p.target == null && msg.isAsynchronous(); + Message prev; + for (;;) { + prev = p; + p = p.next; + if (p == null || when < p.when) { + break; + } + if (needWake && p.isAsynchronous()) { + needWake = false; + } } + msg.next = p; // invariant: p == prev.next + prev.next = msg; + + /* + * If this block is executing then we have a build without tail tracking - + * specifically: Flags.messageQueueTailTracking() == false. This is determined + * at build time so the flag won't change on us during runtime. + * + * Since we don't want to pepper the code with extra checks, we only check + * for tail tracking when we might use mLast. Otherwise, we continue to update + * mLast as the tail of the list. + * + * In this case however we are not maintaining mLast correctly. Since we never + * use it, this is fine. However, we run the risk of leaking a reference. + * So set mLast to null in this case to avoid any Message leaks. The other + * sites will never use the value so we are safe against null pointer derefs. + */ + mLast = null; } - msg.next = p; // invariant: p == prev.next - prev.next = msg; + } + + if (msg.isAsynchronous()) { + mAsyncMessageCount++; } // We can assume mPtr != 0 because mQuitting is false. @@ -692,10 +779,17 @@ public final class MessageQueue { && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -703,8 +797,14 @@ public final class MessageQueue { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -726,10 +826,17 @@ public final class MessageQueue { && (object == null || object.equals(p.obj))) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -737,8 +844,14 @@ public final class MessageQueue { if (n.target == h && n.what == what && (object == null || object.equals(n.obj))) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -760,10 +873,17 @@ public final class MessageQueue { && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -771,8 +891,14 @@ public final class MessageQueue { if (n.target == h && n.callback == r && (object == null || n.obj == object)) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -794,10 +920,17 @@ public final class MessageQueue { && (object == null || object.equals(p.obj))) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; @@ -805,8 +938,14 @@ public final class MessageQueue { if (n.target == h && n.callback == r && (object == null || object.equals(n.obj))) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -829,18 +968,31 @@ public final class MessageQueue { && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -862,18 +1014,31 @@ public final class MessageQueue { && (object == null || object.equals(p.obj))) { Message n = p.next; mMessages = n; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); p = n; } + if (p == null) { + mLast = mMessages; + } + // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || object.equals(n.obj))) { Message nn = n.next; + if (n.isAsynchronous()) { + mAsyncMessageCount--; + } n.recycleUnchecked(); p.next = nn; + if (p.next == null) { + mLast = p; + } continue; } } @@ -890,6 +1055,8 @@ public final class MessageQueue { p = n; } mMessages = null; + mLast = null; + mAsyncMessageCount = 0; } private void removeAllFutureMessagesLocked() { @@ -911,9 +1078,14 @@ public final class MessageQueue { p = n; } p.next = null; + mLast = p; + do { p = n; n = p.next; + if (p.isAsynchronous()) { + mAsyncMessageCount--; + } p.recycleUnchecked(); } while (n != null); } diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java index bb677d529507..a0ab066ffb75 100644 --- a/core/java/android/os/PowerMonitorReadings.java +++ b/core/java/android/os/PowerMonitorReadings.java @@ -71,7 +71,7 @@ public final class PowerMonitorReadings { */ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") @ElapsedRealtimeLong - public long getTimestamp(@NonNull PowerMonitor powerMonitor) { + public long getTimestampMillis(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mTimestampsMs[offset]; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f952fcf36e60..dd0436cbb2f2 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -19,6 +19,7 @@ package android.os; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.annotation.ElapsedRealtimeLong; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -37,6 +38,7 @@ import android.webkit.WebViewZygote; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; +import com.android.sdksandbox.flags.Flags; import dalvik.system.VMRuntime; @@ -1016,11 +1018,30 @@ public class Process { @SystemApi(client = MODULE_LIBRARIES) @TestApi @android.ravenwood.annotation.RavenwoodKeep + // TODO(b/318651609): Deprecate once Process#getSdkSandboxUidForAppUid is rolled out to 100% public static final int toSdkSandboxUid(int uid) { return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID); } /** + * Returns the sdk sandbox uid corresponding to an app uid. + * @see android.app.sdksandbox.SdkSandboxManager + * + * @param uid the app uid + * @return the sdk sandbox uid for the given app uid + * + * @throws IllegalArgumentException if input is not an app uid + */ + @FlaggedApi(Flags.FLAG_SDK_SANDBOX_UID_TO_APP_UID_API) + @android.ravenwood.annotation.RavenwoodKeep + public static final int getSdkSandboxUidForAppUid(int uid) { + if (!isApplicationUid(uid)) { + throw new IllegalArgumentException("Input UID is not an app UID"); + } + return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID); + } + + /** * Returns whether the current process is a sdk sandbox process. */ @android.ravenwood.annotation.RavenwoodKeep diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index d64614628b61..0db90bff48fd 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -98,3 +98,11 @@ flag { description: "Guards StrictMode APIs for detecting restricted network access." bug: "317250784" } + +flag { + name: "message_queue_tail_tracking" + namespace: "system_performance" + description: "track tail of message queue." + bug: "305311707" + is_fixed_read_only: true +} diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index 2d53341be654..322a8e62dbb3 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -25,8 +25,8 @@ import android.content.Context; import android.os.BatteryStats; import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.os.IPowerStatsService; +import android.os.OutcomeReceiver; import android.os.PowerMonitor; import android.os.PowerMonitorReadings; import android.os.Process; @@ -39,6 +39,7 @@ import com.android.internal.app.IBatteryStats; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -161,12 +162,12 @@ public class SystemHealthManager { * (on-device power rail monitor) rails and modeled energy consumers. If ODPM is unsupported * on this device this method delivers an empty list. * - * @param handler optional Handler to deliver the callback. If not supplied, the callback + * @param executor optional Handler to deliver the callback. If not supplied, the callback * may be invoked on an arbitrary thread. * @param onResult callback for the result */ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") - public void getSupportedPowerMonitors(@Nullable Handler handler, + public void getSupportedPowerMonitors(@Nullable Executor executor, @NonNull Consumer<List<PowerMonitor>> onResult) { final List<PowerMonitor> result; synchronized (mPowerMonitorsLock) { @@ -180,15 +181,15 @@ public class SystemHealthManager { } } if (result != null) { - if (handler != null) { - handler.post(() -> onResult.accept(result)); + if (executor != null) { + executor.execute(() -> onResult.accept(result)); } else { onResult.accept(result); } return; } try { - mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) { + mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { PowerMonitor[] array = resultData.getParcelableArray( @@ -197,7 +198,11 @@ public class SystemHealthManager { synchronized (mPowerMonitorsLock) { mPowerMonitorsInfo = result; } - onResult.accept(result); + if (executor != null) { + executor.execute(()-> onResult.accept(result)); + } else { + onResult.accept(result); + } } }); } catch (RemoteException e) { @@ -213,17 +218,22 @@ public class SystemHealthManager { * monitors. * * @param powerMonitors power monitors to be retrieved. - * @param handler optional Handler to deliver the callbacks. If not supplied, the callback - * may be invoked on an arbitrary thread. - * @param onSuccess callback for the result - * @param onError callback invoked in case of an error + * @param executor optional Executor to deliver the callbacks. If not supplied, the + * callback may be invoked on an arbitrary thread. + * @param onResult callback for the result */ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors, - @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess, - @NonNull Consumer<RuntimeException> onError) { + @Nullable Executor executor, + @NonNull OutcomeReceiver<PowerMonitorReadings, RuntimeException> onResult) { if (mPowerStats == null) { - onError.accept(new IllegalArgumentException("Unsupported power monitor")); + IllegalArgumentException error = + new IllegalArgumentException("Unsupported power monitor"); + if (executor != null) { + executor.execute(() -> onResult.onError(error)); + } else { + onResult.onError(error); + } return; } @@ -235,18 +245,31 @@ public class SystemHealthManager { indices[i] = powerMonitorsArray[i].index; } try { - mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) { + mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == IPowerStatsService.RESULT_SUCCESS) { - onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray, + PowerMonitorReadings result = new PowerMonitorReadings(powerMonitorsArray, resultData.getLongArray(IPowerStatsService.KEY_ENERGY), - resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS))); - } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { - onError.accept(new IllegalArgumentException("Unsupported power monitor")); + resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)); + if (executor != null) { + executor.execute(() -> onResult.onResult(result)); + } else { + onResult.onResult(result); + } } else { - onError.accept(new IllegalStateException( - "Unrecognized result code " + resultCode)); + RuntimeException error; + if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { + error = new IllegalArgumentException("Unsupported power monitor"); + } else { + error = new IllegalStateException( + "Unrecognized result code " + resultCode); + } + if (executor != null) { + executor.execute(() -> onResult.onError(error)); + } else { + onResult.onError(error); + } } } }); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f5260ef34986..47065e18b74b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -108,7 +108,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.net.URISyntaxException; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -3586,10 +3585,12 @@ public final class Settings { || applicationInfo.isSignedWithPlatformKey(); } - public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix, - List<String> names) { + private ArrayMap<String, String> getStringsForPrefixStripPrefix( + ContentResolver cr, String prefix, String[] names) { String namespace = prefix.substring(0, prefix.length() - 1); ArrayMap<String, String> keyValues = new ArrayMap<>(); + int substringLength = prefix.length(); + int currentGeneration = -1; boolean needsGenerationTracker = false; @@ -3619,10 +3620,13 @@ public final class Settings { if (DEBUG) { Log.i(TAG, "Cache hit for prefix:" + prefix); } - if (!names.isEmpty()) { + if (names.length > 0) { for (String name : names) { + // mValues can contain "null" values, need to use containsKey. if (mValues.containsKey(name)) { - keyValues.put(name, mValues.get(name)); + keyValues.put( + name.substring(substringLength), + mValues.get(name)); } } } else { @@ -3631,7 +3635,10 @@ public final class Settings { // Explicitly exclude the prefix as it is only there to // signal that the prefix has been cached. if (key.startsWith(prefix) && !key.equals(prefix)) { - keyValues.put(key, mValues.get(key)); + String value = mValues.valueAt(i); + keyValues.put( + key.substring(substringLength), + value); } } } @@ -3691,14 +3698,22 @@ public final class Settings { Map<String, String> flagsToValues = (HashMap) b.getSerializable(Settings.NameValueTable.VALUE, java.util.HashMap.class); // Only the flags requested by the caller - if (!names.isEmpty()) { - for (Map.Entry<String, String> flag : flagsToValues.entrySet()) { - if (names.contains(flag.getKey())) { - keyValues.put(flag.getKey(), flag.getValue()); + if (names.length > 0) { + for (String name : names) { + // flagsToValues can contain "null" values, need to use containsKey. + if (flagsToValues.containsKey(name)) { + keyValues.put( + name.substring(substringLength), + flagsToValues.get(name)); } } } else { - keyValues.putAll(flagsToValues); + keyValues.ensureCapacity(keyValues.size() + flagsToValues.size()); + for (Map.Entry<String, String> flag : flagsToValues.entrySet()) { + keyValues.put( + flag.getKey().substring(substringLength), + flag.getValue()); + } } synchronized (NameValueCache.this) { @@ -12165,6 +12180,36 @@ public final class Settings { */ public static final String HIDE_PRIVATESPACE_ENTRY_POINT = "hide_privatespace_entry_point"; + /** @hide */ + public static final int PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK = 0; + /** @hide */ + public static final int PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY = 1; + /** @hide */ + public static final int PRIVATE_SPACE_AUTO_LOCK_NEVER = 2; + + /** + * The different auto lock options for private space. + * + * @hide + */ + @IntDef(prefix = {"PRIVATE_SPACE_AUTO_LOCK_"}, value = { + PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK, + PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY, + PRIVATE_SPACE_AUTO_LOCK_NEVER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PrivateSpaceAutoLockOption { + } + + + /** + * Store auto lock value for private space. + * The possible values are defined in {@link PrivateSpaceAutoLockOption}. + * + * @hide + */ + public static final String PRIVATE_SPACE_AUTO_LOCK = "private_space_auto_lock"; + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. @@ -19854,21 +19899,15 @@ public final class Settings { @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) public static Map<String, String> getStrings(@NonNull ContentResolver resolver, @NonNull String namespace, @NonNull List<String> names) { - List<String> compositeNames = new ArrayList<>(names.size()); - for (String name : names) { - compositeNames.add(createCompositeName(namespace, name)); + String[] compositeNames = new String[names.size()]; + for (int i = 0, size = names.size(); i < size; ++i) { + compositeNames[i] = createCompositeName(namespace, names.get(i)); } String prefix = createPrefix(namespace); - ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix( + + ArrayMap<String, String> keyValues = sNameValueCache.getStringsForPrefixStripPrefix( resolver, prefix, compositeNames); - int size = rawKeyValues.size(); - int substringLength = prefix.length(); - ArrayMap<String, String> keyValues = new ArrayMap<>(size); - for (int i = 0; i < size; ++i) { - keyValues.put(rawKeyValues.keyAt(i).substring(substringLength), - rawKeyValues.valueAt(i)); - } return keyValues; } @@ -20194,12 +20233,13 @@ public final class Settings { private static String createCompositeName(@NonNull String namespace, @NonNull String name) { Preconditions.checkNotNull(namespace); Preconditions.checkNotNull(name); - return createPrefix(namespace) + name; + var sb = new StringBuilder(namespace.length() + 1 + name.length()); + return sb.append(namespace).append('/').append(name).toString(); } private static String createPrefix(@NonNull String namespace) { Preconditions.checkNotNull(namespace); - return namespace + "/"; + return namespace + '/'; } private static Uri createNamespaceUri(@NonNull String namespace) { diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 7d9c0a37a13f..2d657c2813a5 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -445,6 +445,19 @@ public class DreamService extends Service implements Window.Callback { } /** + * Retrieves the current {@link android.app.Activity} associated with the dream. + * This method behaves similarly to calling {@link android.app.Activity#getActivity()}. + * + * @return The current activity, or null if the dream is not associated with an activity + * or not started. + * + * @hide + */ + public Activity getActivity() { + return mActivity; + } + + /** * Inflates a layout resource and set it to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(int)}. * diff --git a/core/java/android/service/wearable/OWNERS b/core/java/android/service/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/core/java/android/service/wearable/OWNERS +++ b/core/java/android/service/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 4c8188801eff..a6d3bb47d9c8 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -454,7 +454,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT, Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null, mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing); - mMax = (int) Math.ceil(line.metrics(null, null, false)); + mMax = (int) Math.ceil(line.metrics(null, null, false, null)); TextLine.recycle(line); } @@ -603,7 +603,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback 0 /* ellipsisStart, 0 since text has not been ellipsized at this point */, 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */, useFallbackLineSpacing); - fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false)); + fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false, null)); TextLine.recycle(line); return fm; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index c9906cc68f3d..eca848ae1ea3 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -18,6 +18,7 @@ package android.text; import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; +import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION; import android.annotation.FlaggedApi; import android.annotation.FloatRange; @@ -50,8 +51,10 @@ import com.android.internal.util.GrowingArrayUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.text.BreakIterator; import java.util.Arrays; import java.util.List; +import java.util.Locale; /** * A base class that manages text layout in visual elements on @@ -669,7 +672,8 @@ public abstract class Layout { int start = previousLineEnd; previousLineEnd = getLineStart(lineNum + 1); final boolean justify = isJustificationRequired(lineNum); - int end = getLineVisibleEnd(lineNum, start, previousLineEnd); + int end = getLineVisibleEnd(lineNum, start, previousLineEnd, + true /* trailingSpaceAtLastLineIsVisible */); paint.setStartHyphenEdit(getStartHyphenEdit(lineNum)); paint.setEndHyphenEdit(getEndHyphenEdit(lineNum)); @@ -1056,7 +1060,7 @@ public abstract class Layout { if (isJustificationRequired(line)) { tl.justify(getJustifyWidth(line)); } - tl.metrics(null, rectF, false); + tl.metrics(null, rectF, false, null); float lineLeft = rectF.left; float lineRight = rectF.right; @@ -1456,7 +1460,7 @@ public abstract class Layout { tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops, getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line), isFallbackLineSpacingEnabled()); - float wid = tl.measure(offset - start, trailing, null, null); + float wid = tl.measure(offset - start, trailing, null, null, null); TextLine.recycle(tl); if (clamped && wid > mWidth) { @@ -1792,12 +1796,69 @@ public abstract class Layout { if (isJustificationRequired(line)) { tl.justify(getJustifyWidth(line)); } - final float width = tl.metrics(null, null, mUseBoundsForWidth); + final float width = tl.metrics(null, null, mUseBoundsForWidth, null); TextLine.recycle(tl); return width; } /** + * Returns the number of letter spacing unit in the line. + * + * <p> + * This API returns a number of letters that is a target of letter spacing. The letter spacing + * won't be added to the middle of the characters that are needed to be treated as a single, + * e.g., ligatured or conjunct form. Note that this value is different from the number of] + * grapheme clusters that is calculated by {@link BreakIterator#getCharacterInstance(Locale)}. + * For example, if the "fi" is ligatured, the ligatured form is treated as single uni and letter + * spacing is not added, but it has two separate grapheme cluster. + * + * <p> + * This value is used for calculating the letter spacing amount for the justification because + * the letter spacing is applied between clusters. For example, if extra {@code W} pixels needed + * to be filled by letter spacing, the amount of letter spacing to be applied is + * {@code W}/(letter spacing unit count - 1) px. + * + * @param line the index of the line + * @param includeTrailingWhitespace whether to include trailing whitespace + * @return the number of cluster count in the line. + */ + @IntRange(from = 0) + @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION) + public int getLineLetterSpacingUnitCount(@IntRange(from = 0) int line, + boolean includeTrailingWhitespace) { + final int start = getLineStart(line); + final int end = includeTrailingWhitespace ? getLineEnd(line) + : getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1), + false // trailingSpaceAtLastLineIsVisible: Treating trailing whitespaces at + // the last line as a invisible chars for single line justification. + ); + + final Directions directions = getLineDirections(line); + // Returned directions can actually be null + if (directions == null) { + return 0; + } + final int dir = getParagraphDirection(line); + + final TextLine tl = TextLine.obtain(); + final TextPaint paint = mWorkPaint; + paint.set(mPaint); + paint.setStartHyphenEdit(getStartHyphenEdit(line)); + paint.setEndHyphenEdit(getEndHyphenEdit(line)); + tl.set(paint, mText, start, end, dir, directions, + false, null, // tab width is not used for cluster counting. + getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line), + isFallbackLineSpacingEnabled()); + if (mLineInfo == null) { + mLineInfo = new TextLine.LineInfo(); + } + mLineInfo.setClusterCount(0); + tl.metrics(null, null, mUseBoundsForWidth, mLineInfo); + TextLine.recycle(tl); + return mLineInfo.getClusterCount(); + } + + /** * Returns the signed horizontal extent of the specified line, excluding * leading margin. If full is false, excludes trailing whitespace. * @param line the index of the line @@ -1823,7 +1884,7 @@ public abstract class Layout { if (isJustificationRequired(line)) { tl.justify(getJustifyWidth(line)); } - final float width = tl.metrics(null, null, mUseBoundsForWidth); + final float width = tl.metrics(null, null, mUseBoundsForWidth, null); TextLine.recycle(tl); return width; } @@ -2432,14 +2493,21 @@ public abstract class Layout { * is not counted) on the specified line. */ public int getLineVisibleEnd(int line) { - return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1)); + return getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1), + true /* trailingSpaceAtLastLineIsVisible */); } - private int getLineVisibleEnd(int line, int start, int end) { + private int getLineVisibleEnd(int line, int start, int end, + boolean trailingSpaceAtLastLineIsVisible) { CharSequence text = mText; char ch; - if (line == getLineCount() - 1) { - return end; + + // Historically, trailing spaces at the last line is counted as visible. However, this + // doesn't work well for justification. + if (trailingSpaceAtLastLineIsVisible) { + if (line == getLineCount() - 1) { + return end; + } } for (; end > start; end--) { @@ -2939,7 +3007,7 @@ public abstract class Layout { tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops, 0 /* ellipsisStart */, 0 /* ellipsisEnd */, false /* use fallback line spacing. unused */); - return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth)); + return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth, null)); } finally { TextLine.recycle(tl); if (mt != null) { @@ -3337,6 +3405,8 @@ public abstract class Layout { private boolean mUseBoundsForWidth; private @Nullable Paint.FontMetrics mMinimumFontMetrics; + private TextLine.LineInfo mLineInfo = null; + /** @hide */ @IntDef(prefix = { "DIR_" }, value = { DIR_LEFT_TO_RIGHT, diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index f9abec04e71d..135935cb0632 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -76,6 +76,21 @@ public class TextLine { private RectF mTmpRectForPaintAPI; private Rect mTmpRectForPrecompute; + // Recycling object for Paint APIs. Do not use outside getRunAdvances method. + private Paint.RunInfo mRunInfo; + + public static final class LineInfo { + private int mClusterCount; + + public int getClusterCount() { + return mClusterCount; + } + + public void setClusterCount(int clusterCount) { + mClusterCount = clusterCount; + } + }; + private boolean mUseFallbackExtent = false; // The start and end of a potentially existing ellipsis on this text line. @@ -270,7 +285,7 @@ public class TextLine { // width. return; } - final float width = Math.abs(measure(end, false, null, null)); + final float width = Math.abs(measure(end, false, null, null, null)); mAddedWidthForJustify = (justifyWidth - width) / spaces; mIsJustifying = true; } @@ -315,10 +330,12 @@ public class TextLine { * @param drawBounds output parameter for drawing bounding box. optional. * @param returnDrawWidth true for returning width of the bounding box, false for returning * total advances. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width of the line */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth) { + public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth, + @Nullable LineInfo lineInfo) { if (returnDrawWidth) { if (drawBounds == null) { if (mTmpRectForMeasure == null) { @@ -327,7 +344,7 @@ public class TextLine { drawBounds = mTmpRectForMeasure; } drawBounds.setEmpty(); - float w = measure(mLen, false, fmi, drawBounds); + float w = measure(mLen, false, fmi, drawBounds, lineInfo); float boundsWidth = drawBounds.width(); if (Math.abs(w) > boundsWidth) { return w; @@ -337,7 +354,7 @@ public class TextLine { return Math.signum(w) * boundsWidth; } } else { - return measure(mLen, false, fmi, drawBounds); + return measure(mLen, false, fmi, drawBounds, lineInfo); } } @@ -407,12 +424,13 @@ public class TextLine { * the edge of the preceding run's edge. See example above. * @param fmi receives metrics information about the requested character, can be null * @param drawBounds output parameter for drawing bounding box. optional. + * @param lineInfo an optional output parameter for filling line information. * @return the signed graphical offset from the leading margin to the requested character edge. * The positive value means the offset is right from the leading edge. The negative * value means the offset is left from the leading edge. */ public float measure(@IntRange(from = 0) int offset, boolean trailing, - @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds) { + @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable LineInfo lineInfo) { if (offset > mLen) { throw new IndexOutOfBoundsException( "offset(" + offset + ") should be less than line limit(" + mLen + ")"); @@ -437,16 +455,16 @@ public class TextLine { if (targetIsInThisSegment && sameDirection) { return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null, - 0, h); + 0, h, lineInfo); } final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds, - null, 0, h); + null, 0, h, lineInfo); h += sameDirection ? segmentWidth : -segmentWidth; if (targetIsInThisSegment) { return h + measureRun(segStart, offset, j, runIsRtl, null, null, null, 0, - h); + h, lineInfo); } if (j != runLimit) { // charAt(j) == TAB_CHAR @@ -537,7 +555,8 @@ public class TextLine { final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; final float segmentWidth = - measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0); + measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0, + null); final float oldh = h; h += sameDirection ? segmentWidth : -segmentWidth; @@ -578,7 +597,7 @@ public class TextLine { } /** - * @see #measure(int, boolean, FontMetricsInt, RectF) + * @see #measure(int, boolean, FontMetricsInt, RectF, LineInfo) * @return The measure results for all possible offsets */ @VisibleForTesting @@ -610,7 +629,7 @@ public class TextLine { final float previousSegEndHorizontal = measurement[segStart]; final float width = measureRun(segStart, j, j, runIsRtl, fmi, null, measurement, segStart, - 0); + 0, null); horizontal += sameDirection ? width : -width; float currHorizontal = sameDirection ? oldHorizontal : horizontal; @@ -675,14 +694,14 @@ public class TextLine { boolean needWidth) { if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { - float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0); + float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null); handleRun(start, limit, limit, runIsRtl, c, null, x + w, top, - y, bottom, null, null, false, null, 0); + y, bottom, null, null, false, null, 0, null); return w; } return handleRun(start, limit, limit, runIsRtl, c, null, x, top, - y, bottom, null, null, needWidth, null, 0); + y, bottom, null, null, needWidth, null, 0, null); } /** @@ -698,19 +717,20 @@ public class TextLine { * @param advances receives the advance information about the requested run, can be null. * @param advancesIndex the start index to fill in the advance information. * @param x horizontal offset of the run. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width from the start of the run to the leading edge * of the character at offset, based on the run (not paragraph) direction */ private float measureRun(int start, int offset, int limit, boolean runIsRtl, @Nullable FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable float[] advances, - int advancesIndex, float x) { + int advancesIndex, float x, @Nullable LineInfo lineInfo) { if (drawBounds != null && (mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { - float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0); + float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0, null); return handleRun(start, offset, limit, runIsRtl, null, null, x + w, 0, 0, 0, fmi, - drawBounds, true, advances, advancesIndex); + drawBounds, true, advances, advancesIndex, lineInfo); } return handleRun(start, offset, limit, runIsRtl, null, null, x, 0, 0, 0, fmi, drawBounds, - true, advances, advancesIndex); + true, advances, advancesIndex, lineInfo); } /** @@ -729,14 +749,14 @@ public class TextLine { int limit, boolean runIsRtl, float x, boolean needWidth) { if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { - float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0); + float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null); handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, null, - false, null, 0); + false, null, 0, null); return w; } return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null, null, - needWidth, null, 0); + needWidth, null, 0, null); } @@ -1077,16 +1097,35 @@ public class TextLine { private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd, boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex, - RectF drawingBounds) { + RectF drawingBounds, @Nullable LineInfo lineInfo) { + if (lineInfo != null) { + if (mRunInfo == null) { + mRunInfo = new Paint.RunInfo(); + } + mRunInfo.setClusterCount(0); + } else { + mRunInfo = null; + } if (mCharsValid) { - return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd, - runIsRtl, offset, advances, advancesIndex, drawingBounds); + float r = wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd, + runIsRtl, offset, advances, advancesIndex, drawingBounds, mRunInfo); + if (lineInfo != null) { + lineInfo.setClusterCount(lineInfo.getClusterCount() + mRunInfo.getClusterCount()); + } + return r; } else { final int delta = mStart; - if (mComputed == null || advances != null) { - return wp.getRunCharacterAdvance(mText, delta + start, delta + end, + // TODO: Add cluster information to the PrecomputedText for better performance of + // justification. + if (mComputed == null || advances != null || lineInfo != null) { + float r = wp.getRunCharacterAdvance(mText, delta + start, delta + end, delta + contextStart, delta + contextEnd, runIsRtl, - delta + offset, advances, advancesIndex, drawingBounds); + delta + offset, advances, advancesIndex, drawingBounds, mRunInfo); + if (lineInfo != null) { + lineInfo.setClusterCount( + lineInfo.getClusterCount() + mRunInfo.getClusterCount()); + } + return r; } else { if (drawingBounds != null) { if (mTmpRectForPrecompute == null) { @@ -1120,6 +1159,7 @@ public class TextLine { * @param decorations the list of locations and paremeters for drawing decorations * @param advances receives the advance information about the requested run, can be null. * @param advancesIndex the start index to fill in the advance information. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width of the run based on the run direction; only * valid if needWidth is true */ @@ -1128,7 +1168,7 @@ public class TextLine { Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth, int offset, @Nullable ArrayList<DecorationInfo> decorations, - @Nullable float[] advances, int advancesIndex) { + @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) { if (mIsJustifying) { wp.setWordSpacing(mAddedWidthForJustify); @@ -1155,7 +1195,8 @@ public class TextLine { mTmpRectForPaintAPI = new RectF(); } totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset, - advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI); + advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI, + lineInfo); if (drawBounds != null) { if (runIsRtl) { mTmpRectForPaintAPI.offset(x - totalWidth, 0); @@ -1206,9 +1247,9 @@ public class TextLine { final int decorationStart = Math.max(info.start, start); final int decorationEnd = Math.min(info.end, offset); float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart, - contextEnd, runIsRtl, decorationStart, null, 0, null); + contextEnd, runIsRtl, decorationStart, null, 0, null, null); float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart, - contextEnd, runIsRtl, decorationEnd, null, 0, null); + contextEnd, runIsRtl, decorationEnd, null, 0, null, null); final float decorationXLeft, decorationXRight; if (runIsRtl) { decorationXLeft = rightX - decorationEndAdvance; @@ -1377,6 +1418,7 @@ public class TextLine { * @param needWidth true if the width is required * @param advances receives the advance information about the requested run, can be null. * @param advancesIndex the start index to fill in the advance information. + * @param lineInfo an optional output parameter for filling line information. * @return the signed width of the run based on the run direction; only * valid if needWidth is true */ @@ -1384,7 +1426,7 @@ public class TextLine { int limit, boolean runIsRtl, Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth, - @Nullable float[] advances, int advancesIndex) { + @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) { if (measureLimit < start || measureLimit > limit) { throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of " @@ -1431,7 +1473,7 @@ public class TextLine { wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit())); return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top, y, bottom, fmi, drawBounds, needWidth, measureLimit, null, advances, - advancesIndex); + advancesIndex, lineInfo); } // Shaping needs to take into account context up to metric boundaries, @@ -1523,7 +1565,7 @@ public class TextLine { consumer, x, top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit, Math.min(activeEnd, mlimit), mDecorations, - advances, advancesIndex + activeStart - start); + advances, advancesIndex + activeStart - start, lineInfo); activeStart = j; activePaint.set(wp); @@ -1551,7 +1593,7 @@ public class TextLine { x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x, top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit, Math.min(activeEnd, mlimit), mDecorations, - advances, advancesIndex + activeStart - start); + advances, advancesIndex + activeStart - start, lineInfo); } return x - originalX; diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 9bf43a390d70..1d81be17f580 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -370,4 +370,14 @@ interface IWindowSession { boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow); boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, IBinder transferTouchToken); + + /** + * Moves the focus to the adjacent window if there is one in the given direction. This can only + * move the focus to the window in the same leaf task. + * + * @param fromWindow The calling window that the focus is moved from. + * @param direction The {@link android.view.View.FocusDirection} that the new focus should go. + * @return {@code true} if the focus changes. Otherwise, {@code false}. + */ + boolean moveFocusToAdjacentWindow(IWindow fromWindow, int direction); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 350876c828b7..c66f3c8032fd 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,6 +16,7 @@ package android.view; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS; import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED; import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND; @@ -7236,7 +7237,7 @@ public final class ViewRootImpl implements ViewParent, } private boolean performFocusNavigation(KeyEvent event) { - int direction = 0; + @FocusDirection int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: if (event.hasNoModifiers()) { @@ -7288,6 +7289,8 @@ public final class ViewRootImpl implements ViewParent, isFastScrolling)); return true; } + } else if (moveFocusToAdjacentWindow(direction)) { + return true; } // Give the focused view a last chance to handle the dpad key. @@ -7297,12 +7300,26 @@ public final class ViewRootImpl implements ViewParent, } else { if (mView.restoreDefaultFocus()) { return true; + } else if (moveFocusToAdjacentWindow(direction)) { + return true; } } } return false; } + private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) { + if (getConfiguration().windowConfiguration.getWindowingMode() + != WINDOWING_MODE_MULTI_WINDOW) { + return false; + } + try { + return mWindowSession.moveFocusToAdjacentWindow(mWindow, direction); + } catch (RemoteException e) { + return false; + } + } + private boolean performKeyboardGroupNavigation(int direction) { final View focused = mView.findFocus(); if (focused == null && mView.restoreDefaultFocus()) { diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index d6ac56239aed..b95e4595d6b9 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -30,6 +30,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Log; import android.util.MergedConfiguration; +import android.view.View.FocusDirection; import android.view.WindowInsets.Type.InsetsType; import android.window.ClientWindowFrames; import android.window.OnBackInvokedCallbackInfo; @@ -665,6 +666,13 @@ public class WindowlessWindowManager implements IWindowSession { return false; } + @Override + public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) { + Log.e(TAG, "Received request to moveFocusToAdjacentWindow on" + + " WindowlessWindowManager. We shouldn't get here!"); + return false; + } + void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) { IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder(); IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder(); diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 70d8abe1dc27..57a3b765641d 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -109,6 +109,40 @@ public class AnimationUtils { } /** + * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current + * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses + * during a vsync update are synchronized to the timestamp of the vsync. + * + * It is also exposed to tests to allow for rapid, flake-free headless testing. + * + * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to + * progress. Failing to do this will result in stuck animations, scrolls, and flings. + * + * Note that time is not allowed to "rewind" and must perpetually flow forward. So the + * lock may fail if the time is in the past from a previously returned value, however + * time will be frozen for the duration of the lock. The clock is a thread-local, so + * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and + * {@link #currentAnimationTimeMillis()} are all called on the same thread. + * + * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()} + * will unlock the clock for everyone on the same thread. It is therefore recommended + * for tests to use their own thread to ensure that there is no collision with any existing + * {@link android.view.Choreographer} instance. + * + * Have to add the method back because of b/307888459. + * Remove this method once the lockAnimationClock(long, long) change + * is landed to aosp/android14-tests-dev branch. + * + * @hide + */ + @TestApi + public static void lockAnimationClock(long vsyncMillis) { + AnimationState state = sAnimationState.get(); + state.animationClockLocked = true; + state.currentVsyncTimeMillis = vsyncMillis; + } + + /** * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called * to allow the animation clock to self-update. * diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index a74b06a491e8..9f9b7b4b68a9 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -1,6 +1,13 @@ package: "android.view.flags" flag { + name: "enable_surface_native_alloc_registration" + namespace: "toolkit" + description: "Feature flag for registering surfaces with the VM for faster cleanup" + bug: "306193257" +} + +flag { name: "enable_use_measure_cache_during_force_layout" namespace: "toolkit" description: "Enables using the measure cache during a view force layout from the second " diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index ef5004536354..1d2f65353a65 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -16,6 +16,9 @@ package android.view.textclassifier; +import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -108,6 +111,9 @@ public interface TextClassifier { String TYPE_DATE_TIME = "datetime"; /** Flight number in IATA format. */ String TYPE_FLIGHT_NUMBER = "flight"; + /** One-time login codes */ + @FlaggedApi(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS) + String TYPE_OTP_CODE = "otp_code"; /** * Word that users may be interested to look up for meaning. * @hide @@ -126,7 +132,8 @@ public interface TextClassifier { TYPE_DATE, TYPE_DATE_TIME, TYPE_FLIGHT_NUMBER, - TYPE_DICTIONARY + TYPE_DICTIONARY, + TYPE_OTP_CODE }) @interface EntityType {} diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index bdaad2b68fc2..473b814fc4a7 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -47,6 +47,7 @@ import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -337,7 +338,14 @@ public final class SplashScreenView extends FrameLayout { "SplashScreenView"); ImageView imageView = new ImageView(viewContext); imageView.setBackground(mIconDrawable); - viewHost.setView(imageView, mIconSize, mIconSize); + final int windowFlag = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + final WindowManager.LayoutParams lp = + new WindowManager.LayoutParams(mIconSize, mIconSize, + WindowManager.LayoutParams.TYPE_APPLICATION, windowFlag, + PixelFormat.TRANSPARENT); + viewHost.setView(imageView, lp); SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage(); surfaceView.setChildSurfacePackage(surfacePackage); view.mSurfacePackage = surfacePackage; diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index f2bce9c44001..bb16ad2cb8de 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -72,7 +72,7 @@ flag { name: "predictive_back_system_animations" namespace: "systemui" description: "Predictive back for system animations" - bug: "309545085" + bug: "319421778" is_fixed_read_only: true } diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp index b6517117ca62..a5b2f65eafc7 100644 --- a/core/jni/android_os_VintfObject.cpp +++ b/core/jni/android_os_VintfObject.cpp @@ -96,8 +96,11 @@ static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass) static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv* env, jclass) { std::string error; + // Use temporary VintfObject, not the shared instance, to release memory + // after check. int32_t status = - VintfObject::GetInstance() + VintfObject::Builder() + .build() ->checkCompatibility(&error, ENABLE_ALL_CHECKS.disableAvb().disableKernel()); if (status) LOG(WARNING) << "VintfObject.verifyBuildAtBoot() returns " << status << ": " << error; diff --git a/core/proto/OWNERS b/core/proto/OWNERS index db391f7a8c35..a854e3626e78 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -18,6 +18,7 @@ per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/a per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com per-file background_install_control.proto = wenhaowang@google.com,georgechan@google.com,billylau@google.com +per-file android/content/intent.proto = file:/PACKAGE_MANAGER_OWNERS # Biometrics jaggies@google.com @@ -31,5 +32,3 @@ jreck@google.com # Accessibility pweaver@google.com -hongmingjin@google.com -cbrower@google.com diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ef6caefd3daf..5f3f6419418a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5214,6 +5214,14 @@ <permission android:name="android.permission.BIND_REMOTE_DISPLAY" android:protectionLevel="signature" /> + <!-- Must be required by a android.media.tv.ad.TvAdService to ensure that only the system can + bind to it. + <p>Protection level: signature|privileged + @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") + --> + <permission android:name="android.permission.BIND_TV_AD_SERVICE" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by a {@link android.media.tv.TvInputService} to ensure that only the system can bind to it. <p>Protection level: signature|privileged diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml index af305329da1a..31acd9af164c 100644 --- a/core/res/res/values-watch/config.xml +++ b/core/res/res/values-watch/config.xml @@ -90,4 +90,7 @@ {@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER} devices. --> <bool name="config_viewRotaryEncoderHapticScrollFedbackEnabled">true</bool> + + <!-- If this is true, allow wake from theater mode from motion. --> + <bool name="config_allowTheaterModeWakeFromMotion">true</bool> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 542e9d6f936f..f2858066b55b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1825,7 +1825,7 @@ <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> <string name="fingerprint_acquired_partial">Press firmly on the sensor</string> <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized --> - <string name="fingerprint_acquired_insufficient">Can\u2019t recognize fingerprint. Try again.</string> + <string name="fingerprint_acquired_insufficient">Fingerprint not recognized. Try again.</string> <!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning --> <string name="fingerprint_acquired_imager_dirty">Clean fingerprint sensor and try again</string> <string name="fingerprint_acquired_imager_dirty_alt">Clean sensor and try again</string> @@ -1959,7 +1959,7 @@ <!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] --> <string name="face_acquired_recalibrate">Please re-enroll your face.</string> <!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] --> - <string name="face_acquired_too_different">Can\u2019t recognize face. Try again.</string> + <string name="face_acquired_too_different">Face not recognized. Try again.</string> <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] --> <string name="face_acquired_too_similar">Change the position of your head slightly</string> <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java index bd5f809dc689..e118c98dd4da 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java @@ -255,6 +255,7 @@ public class SQLiteDatabaseTest { final String query = "--comment\nSELECT count(*) from t1"; + database.beginTransactionReadOnly(); try { for (int i = count; i > 0; i--) { ticker.arriveAndAwaitAdvance(); @@ -268,6 +269,7 @@ public class SQLiteDatabaseTest { } catch (Throwable t) { errors.add(t); } finally { + database.endTransaction(); ticker.arriveAndDeregister(); } } diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java index bf56df1c9441..0dec756d7611 100644 --- a/core/tests/coretests/src/android/graphics/PaintTest.java +++ b/core/tests/coretests/src/android/graphics/PaintTest.java @@ -19,6 +19,7 @@ package android.graphics; import static org.junit.Assert.assertNotEquals; import android.test.InstrumentationTestCase; +import android.text.TextUtils; import androidx.test.filters.SmallTest; @@ -362,4 +363,44 @@ public class PaintTest extends InstrumentationTestCase { // = 30 assertEquals(30.0f, p.getUnderlineThickness(), 0.5f); } + + private int getClusterCount(Paint p, String text) { + Paint.RunInfo runInfo = new Paint.RunInfo(); + p.getRunCharacterAdvance(text, 0, text.length(), 0, text.length(), false, 0, null, 0, null, + runInfo); + int ccByString = runInfo.getClusterCount(); + runInfo.setClusterCount(0); + char[] buf = new char[text.length()]; + TextUtils.getChars(text, 0, text.length(), buf, 0); + p.getRunCharacterAdvance(buf, 0, buf.length, 0, buf.length, false, 0, null, 0, null, + runInfo); + int ccByChars = runInfo.getClusterCount(); + assertEquals(ccByChars, ccByString); + return ccByChars; + } + + public void testCluster() { + final Paint p = new Paint(); + p.setTextSize(100); + + // Regular String + assertEquals(1, getClusterCount(p, "A")); + assertEquals(2, getClusterCount(p, "AB")); + + // Ligature is in the same cluster + assertEquals(1, getClusterCount(p, "fi")); // Ligature + p.setFontFeatureSettings("'liga' off"); + assertEquals(2, getClusterCount(p, "fi")); // Ligature is disabled + p.setFontFeatureSettings(""); + + // Combining character + assertEquals(1, getClusterCount(p, "\u0061\u0300")); // A + COMBINING GRAVE ACCENT + + // BiDi + final String rtlStr = "\u05D0\u05D1\u05D2"; + final String ltrStr = "abc"; + assertEquals(3, getClusterCount(p, rtlStr)); + assertEquals(6, getClusterCount(p, rtlStr + ltrStr)); + assertEquals(9, getClusterCount(p, ltrStr + rtlStr + ltrStr)); + } } diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java index 34842a0b7597..a31992c8cfa1 100644 --- a/core/tests/coretests/src/android/text/TextLineTest.java +++ b/core/tests/coretests/src/android/text/TextLineTest.java @@ -50,11 +50,11 @@ public class TextLineTest { tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 0, 0 /* no ellipsis */, false /* useFallbackLinespace */); - final float originalWidth = tl.metrics(null, null, false); + final float originalWidth = tl.metrics(null, null, false, null); final float expandedWidth = 2 * originalWidth; tl.justify(expandedWidth); - final float newWidth = tl.metrics(null, null, false); + final float newWidth = tl.metrics(null, null, false, null); TextLine.recycle(tl); return Math.abs(newWidth - expandedWidth) < 0.5; } @@ -128,7 +128,7 @@ public class TextLineTest { private void assertMeasurements(final TextLine tl, final int length, boolean trailing, final float[] expected) { for (int offset = 0; offset <= length; ++offset) { - assertEquals(expected[offset], tl.measure(offset, trailing, null, null), 0.0f); + assertEquals(expected[offset], tl.measure(offset, trailing, null, null, null), 0.0f); } final boolean[] trailings = new boolean[length + 1]; @@ -318,7 +318,7 @@ public class TextLineTest { tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 9, 12, false /* useFallbackLineSpacing */); - tl.measure(text.length(), false /* trailing */, null /* fmi */, null); + tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null); assertFalse(span.mIsUsed); } @@ -335,7 +335,7 @@ public class TextLineTest { tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 9, 12, false /* useFallbackLineSpacing */); - tl.measure(text.length(), false /* trailing */, null /* fmi */, null); + tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null); assertTrue(span.mIsUsed); } @@ -352,7 +352,7 @@ public class TextLineTest { tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */, 9, 12, false /* useFallbackLineSpacing */); - tl.measure(text.length(), false /* trailing */, null /* fmi */, null); + tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null); assertTrue(span.mIsUsed); } diff --git a/data/keyboards/Vendor_0957_Product_0031.idc b/data/keyboards/Vendor_0957_Product_0031.idc new file mode 100644 index 000000000000..f0320c8a78d4 --- /dev/null +++ b/data/keyboards/Vendor_0957_Product_0031.idc @@ -0,0 +1,21 @@ +# Copyright 2024 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. +# + +# Input Device Configuration file for Google Reference RCU Remote. +# PID 0031 is for old GPIO pin + +# Basic Parameters +keyboard.doNotWakeByDefault = 1 +audio.mic = 1
\ No newline at end of file diff --git a/data/keyboards/Vendor_0957_Product_0034.idc b/data/keyboards/Vendor_0957_Product_0034.idc new file mode 100644 index 000000000000..52ed0bc74fd4 --- /dev/null +++ b/data/keyboards/Vendor_0957_Product_0034.idc @@ -0,0 +1,23 @@ +# Copyright 2024 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. +# + +# Input Device Configuration file for Google Reference RCU Remote. +# PID 0034 is for new GPIO pin PCB. + +# Basic Parameters +keyboard.layout = Vendor_0957_Product_0031 +# The reason why we set is follow https://docs.partner.android.com/tv/build/gtv/boot-resume +keyboard.doNotWakeByDefault = 1 +audio.mic = 1
\ No newline at end of file diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index f10cdb82022e..c5a2f983ae00 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -65,6 +65,8 @@ public class Paint { private long mNativeShader; private long mNativeColorFilter; + private static boolean sIsRobolectric = Build.FINGERPRINT.equals("robolectric"); + // Use a Holder to allow static initialization of Paint in the boot image. private static class NoImagePreloadHolder { public static final NativeAllocationRegistry sRegistry = @@ -2474,6 +2476,19 @@ public class Paint { nGetFontMetricsInt(mNativePaint, metrics, true); } + /** @hide */ + public static final class RunInfo { + private int mClusterCount = 0; + + public int getClusterCount() { + return mClusterCount; + } + + public void setClusterCount(int clusterCount) { + mClusterCount = clusterCount; + } + } + /** * Return the recommend line spacing based on the current typeface and * text size. @@ -3320,7 +3335,7 @@ public class Paint { int contextEnd, boolean isRtl, int offset, @Nullable float[] advances, int advancesIndex) { return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset, - advances, advancesIndex, null); + advances, advancesIndex, null, null); } /** @@ -3339,12 +3354,14 @@ public class Paint { * @param advances the array that receives the computed character advances * @param advancesIndex the start index from which the advances array is filled * @param drawBounds the output parameter for the bounding box of drawing text, optional + * @param runInfo the output parameter for storing run information. * @return width measurement between start and offset - * @hide + * @hide TODO: Reorganize APIs */ public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, - @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) { + @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds, + @Nullable RunInfo runInfo) { if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -3370,11 +3387,19 @@ public class Paint { } if (end == start) { + if (runInfo != null) { + runInfo.setClusterCount(0); + } return 0.0f; } - return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd, - isRtl, offset, advances, advancesIndex, drawBounds); + if (sIsRobolectric) { + return nGetRunCharacterAdvance(mNativePaint, text, start, end, + contextStart, contextEnd, isRtl, offset, advances, advancesIndex, drawBounds); + } else { + return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd, + isRtl, offset, advances, advancesIndex, drawBounds, runInfo); + } } /** @@ -3402,7 +3427,7 @@ public class Paint { int contextStart, int contextEnd, boolean isRtl, int offset, @Nullable float[] advances, int advancesIndex) { return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset, - advances, advancesIndex, null); + advances, advancesIndex, null, null); } /** @@ -3418,12 +3443,14 @@ public class Paint { * @param advances the array that receives the computed character advances * @param advancesIndex the start index from which the advances array is filled * @param drawBounds the output parameter for the bounding box of drawing text, optional + * @param runInfo an optional output parameter for filling run information. * @return width measurement between start and offset - * @hide + * @hide TODO: Reorganize APIs */ public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, - @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) { + @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds, + @Nullable RunInfo runInfo) { if (text == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -3456,7 +3483,7 @@ public class Paint { TextUtils.getChars(text, contextStart, contextEnd, buf, 0); final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart, 0, contextEnd - contextStart, isRtl, offset - contextStart, - advances, advancesIndex, drawBounds); + advances, advancesIndex, drawBounds, runInfo); TemporaryBuffer.recycle(buf); return result; } @@ -3574,7 +3601,7 @@ public class Paint { int contextStart, int contextEnd, boolean isRtl, int offset); private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances, - int advancesIndex, RectF drawingBounds); + int advancesIndex, RectF drawingBounds, RunInfo runInfo); private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance); private static native void nGetFontMetricsIntForText(long paintPtr, char[] text, @@ -3729,4 +3756,11 @@ public class Paint { private static native void nSetTextSize(long paintPtr, float textSize); @CriticalNative private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr); + + + // Following Native methods are kept for old Robolectric JNI signature used by + // SystemUIGoogleRoboRNGTests + private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, + int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, + float[] advances, int advancesIndex, RectF drawingBounds); } diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 5ad144d50b87..45540e0fbbb8 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -175,3 +175,74 @@ android_library { plugins: ["dagger2-compiler"], use_resource_processor: true, } + +android_app { + name: "WindowManagerShellRobolectric", + platform_apis: true, + static_libs: [ + "WindowManager-Shell", + ], + manifest: "multivalentTests/AndroidManifestRobolectric.xml", + use_resource_processor: true, +} + +android_robolectric_test { + name: "WMShellRobolectricTests", + instrumentation_for: "WindowManagerShellRobolectric", + upstream: true, + java_resource_dirs: [ + "multivalentTests/robolectric/config", + ], + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-robolectric-prebuilt", + "mockito-kotlin2", + "truth", + ], +} + +android_test { + name: "WMShellMultivalentTestsOnDevice", + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "frameworks-base-testutils", + "mockito-kotlin2", + "mockito-target-extended-minus-junit4", + "truth", + "platform-test-annotations", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + kotlincflags: ["-Xjvm-default=all"], + optimize: { + enabled: false, + }, + test_suites: ["device-tests"], + platform_apis: true, + certificate: "platform", + aaptflags: [ + "--extra-packages", + "com.android.wm.shell", + ], + manifest: "multivalentTests/AndroidManifest.xml", +} diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml new file mode 100644 index 000000000000..f8f8338e5f04 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml @@ -0,0 +1,13 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> + + <application android:debuggable="true" android:supportsRtl="true" > + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Multivalent tests for WindowManager-Shell" + android:targetPackage="com.android.wm.shell.multivalenttests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml new file mode 100644 index 000000000000..ffcd7d46fbae --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml @@ -0,0 +1,3 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml new file mode 100644 index 000000000000..36fe8ec3370d --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml @@ -0,0 +1,31 @@ +<?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 Tests for WindowManagerShellLib"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="WMShellMultivalentTestsOnDevice.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="WMShellMultivalentTestsOnDevice" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.wm.shell.multivalenttests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties new file mode 100644 index 000000000000..7a0527ccaafb --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties @@ -0,0 +1,2 @@ +sdk=NEWEST_SDK + diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt new file mode 100644 index 000000000000..ea7c6edd4b56 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt @@ -0,0 +1,481 @@ +/* + * 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.wm.shell.bubbles + +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.graphics.Insets +import android.graphics.PointF +import android.graphics.Rect +import android.os.UserHandle +import android.view.WindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.wm.shell.R +import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests operations and the resulting state managed by [BubblePositioner]. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubblePositionerTest { + + private lateinit var positioner: BubblePositioner + private val context = ApplicationProvider.getApplicationContext<Context>() + private val defaultDeviceConfig = + DeviceConfig( + windowBounds = Rect(0, 0, 1000, 2000), + isLargeScreen = false, + isSmallTablet = false, + isLandscape = false, + isRtl = false, + insets = Insets.of(0, 0, 0, 0) + ) + + @Before + fun setUp() { + val windowManager = context.getSystemService(WindowManager::class.java) + positioner = BubblePositioner(context, windowManager) + } + + @Test + fun testUpdate() { + val insets = Insets.of(10, 20, 5, 15) + val screenBounds = Rect(0, 0, 1000, 1200) + val availableRect = Rect(screenBounds) + availableRect.inset(insets) + positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds)) + assertThat(positioner.availableRect).isEqualTo(availableRect) + assertThat(positioner.isLandscape).isFalse() + assertThat(positioner.isLargeScreen).isFalse() + assertThat(positioner.insets).isEqualTo(insets) + } + + @Test + fun testShowBubblesVertically_phonePortrait() { + positioner.update(defaultDeviceConfig) + assertThat(positioner.showBubblesVertically()).isFalse() + } + + @Test + fun testShowBubblesVertically_phoneLandscape() { + positioner.update(defaultDeviceConfig.copy(isLandscape = true)) + assertThat(positioner.isLandscape).isTrue() + assertThat(positioner.showBubblesVertically()).isTrue() + } + + @Test + fun testShowBubblesVertically_tablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + assertThat(positioner.showBubblesVertically()).isTrue() + } + + /** If a resting position hasn't been set, calling it will return the default position. */ + @Test + fun testGetRestingPosition_returnsDefaultPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = positioner.getRestingPosition() + val defaultPosition = positioner.defaultStartPosition + assertThat(restingPosition).isEqualTo(defaultPosition) + } + + /** If a resting position has been set, it'll return that instead of the default position. */ + @Test + fun testGetRestingPosition_returnsRestingPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = PointF(100f, 100f) + positioner.restingPosition = restingPosition + assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition) + } + + /** Test that the default resting position on phone is in upper left. */ + @Test + fun testGetRestingPosition_bubble_onPhone() { + positioner.update(defaultDeviceConfig) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_bubble_onPhone_RTL() { + positioner.update(defaultDeviceConfig.copy(isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle left. */ + @Test + fun testGetRestingPosition_chatBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_chatBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle right. */ + @Test + fun testGetDefaultPosition_appBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_appBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testHasUserModifiedDefaultPosition_false() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = positioner.defaultStartPosition + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + } + + @Test + fun testHasUserModifiedDefaultPosition_true() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = PointF(0f, 100f) + assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue() + } + + @Test + fun testGetExpandedViewHeight_max() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT) + } + + @Test + fun testGetExpandedViewHeight_customHeight_valid() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + minHeight + 100 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + assertThat(positioner.getExpandedViewHeight(bubble)) + .isEqualTo(bubble.getDesiredHeight(context)) + } + + @Test + fun testGetExpandedViewHeight_customHeight_tooSmall() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + 10 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight) + } + + @Test + fun testGetMaxExpandedViewHeight_onLargeTablet() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + val expandedViewPadding = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding) + val expectedHeight = + 1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2 + assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */)) + .isEqualTo(expectedHeight) + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_true() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isTrue() + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_landscape_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_smallTablet_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_phone_false() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testExpandedViewY_phoneLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height so it'll always be top aligned + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_phonePortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // Always top aligned in phone portrait + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on landscape, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val manageButtonPlusMargin = + manageButtonHeight + + 2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + + val expectedExpandedViewY = + positioner.availableRect.bottom - + manageButtonPlusMargin - + positioner.getExpandedViewHeightForLargeScreen() - + pointerWidth + + // Bubbles are bottom aligned on portrait, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(expectedExpandedViewY) + } + + private val defaultYPosition: Float + /** + * Calculates the Y position bubbles should be placed based on the config. Based on the + * calculations in [BubblePositioner.getDefaultStartPosition] and + * [BubbleStackView.RelativeStackPosition]. + */ + get() { + val isTablet = positioner.isLargeScreen + + // On tablet the position is centered, on phone it is an offset from the top. + val desiredY = + if (isTablet) { + positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f + } else { + context.resources + .getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y) + .toFloat() + } + // Since we're visually centering the bubbles on tablet, use total screen height rather + // than the available height. + val height = + if (isTablet) { + positioner.screenRect.height() + } else { + positioner.availableRect.height() + } + val offsetPercent = (desiredY / height).coerceIn(0f, 1f) + val allowableStackRegion = + positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent + } +} diff --git a/libs/WindowManager/Shell/multivalentTestsForDevice b/libs/WindowManager/Shell/multivalentTestsForDevice new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDevice @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file diff --git a/libs/WindowManager/Shell/multivalentTestsForDeviceless b/libs/WindowManager/Shell/multivalentTestsForDeviceless new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDeviceless @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file 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 06210ff98642..44ee5615ac74 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 @@ -331,7 +331,9 @@ class ActivityEmbeddingAnimationRunner { if (!animation.hasExtension()) { continue; } - if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT)) { + if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT) + && adapter.mChange.getActivityComponent() != null) { + // Skip edge extension for translucent activity. continue; } final TransitionInfo.Change change = adapter.mChange; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 81d963877e4c..bb433dbbd2ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -176,6 +176,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private StatusBarCustomizer mCustomizer; private boolean mTrackingLatency; + // Keep previous navigation type before remove mBackNavigationInfo. + @BackNavigationInfo.BackTargetType + private int mPreviousNavigationType; + public BackAnimationController( @NonNull ShellInit shellInit, @NonNull ShellController shellController, @@ -871,6 +875,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellBackAnimationRegistry.resetDefaultCrossActivity(); cancelLatencyTracking(); if (mBackNavigationInfo != null) { + mPreviousNavigationType = mBackNavigationInfo.getType(); mBackNavigationInfo.onBackNavigationFinished(triggerBack); mBackNavigationInfo = null; } @@ -983,7 +988,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellExecutor.execute( () -> { if (!mShellBackAnimationRegistry.cancel( - mBackNavigationInfo.getType())) { + mBackNavigationInfo != null + ? mBackNavigationInfo.getType() + : mPreviousNavigationType)) { return; } if (!mBackGestureStarted) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 80fc3a867d48..ac2a1c867462 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -136,6 +136,9 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds()); mStartTaskRect.offsetTo(0, 0); + // inset bottom in case of pinned taskbar being present + mStartTaskRect.inset(0, 0, 0, mClosingTarget.contentInsets.bottom); + // Draw background. mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(), BACKGROUNDCOLOR, mTransaction); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index bc1a57572c63..5de8a9be9576 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -481,18 +481,20 @@ class SplitScreenTransitions { private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) { final float end = show ? 1.f : 0.f; final float start = 1.f - end; - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(start, end); va.setDuration(FADE_DURATION); va.setInterpolator(show ? ALPHA_IN : ALPHA_OUT); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); transaction.apply(); + mTransactionPool.release(transaction); }); va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); transaction.setAlpha(leash, end); transaction.apply(); mTransactionPool.release(transaction); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index db845137540b..8c861c63a70d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -124,6 +124,16 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, int mAnimType = ANIM_TYPE_DEFAULT; final IBinder mTransition; + private final Transitions mPlayer; + private final DefaultMixedHandler mMixedHandler; + private final PipTransitionController mPipHandler; + private final RecentsTransitionHandler mRecentsHandler; + private final StageCoordinator mSplitHandler; + private final KeyguardTransitionHandler mKeyguardHandler; + private final DesktopTasksController mDesktopTasksController; + private final UnfoldTransitionHandler mUnfoldHandler; + private final ActivityEmbeddingController mActivityEmbeddingController; + Transitions.TransitionHandler mLeftoversHandler = null; TransitionInfo mInfo = null; WindowContainerTransaction mFinishWCT = null; @@ -144,9 +154,386 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, */ int mInFlightSubAnimations = 0; - MixedTransition(int type, IBinder transition) { + MixedTransition(int type, IBinder transition, Transitions player, + DefaultMixedHandler mixedHandler, PipTransitionController pipHandler, + RecentsTransitionHandler recentsHandler, StageCoordinator splitHandler, + KeyguardTransitionHandler keyguardHandler, + DesktopTasksController desktopTasksController, + UnfoldTransitionHandler unfoldHandler, + ActivityEmbeddingController activityEmbeddingController) { mType = type; mTransition = transition; + mPlayer = player; + mMixedHandler = mixedHandler; + mPipHandler = pipHandler; + mRecentsHandler = recentsHandler; + mSplitHandler = splitHandler; + mKeyguardHandler = keyguardHandler; + mDesktopTasksController = desktopTasksController; + mUnfoldHandler = unfoldHandler; + mActivityEmbeddingController = activityEmbeddingController; + + switch (type) { + case TYPE_RECENTS_DURING_DESKTOP: + case TYPE_RECENTS_DURING_KEYGUARD: + case TYPE_RECENTS_DURING_SPLIT: + mLeftoversHandler = mRecentsHandler; + break; + case TYPE_UNFOLD: + mLeftoversHandler = mUnfoldHandler; + break; + case TYPE_DISPLAY_AND_SPLIT_CHANGE: + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: + case TYPE_ENTER_PIP_FROM_SPLIT: + case TYPE_KEYGUARD: + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + default: + break; + } + } + + boolean startAnimation( + @NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { + return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, + finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + return animateEnterPipFromActivityEmbedding( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { + return false; + } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { + final boolean handledToPip = animateOpenIntentWithRemoteAndPip( + info, startTransaction, finishTransaction, finishCallback); + // Consume the transition on remote handler if the leftover handler already handle + // this transition. And if it cannot, the transition will be handled by remote + // handler, so don't consume here. + // Need to check leftOverHandler as it may change in + // #animateOpenIntentWithRemoteAndPip + if (handledToPip && mHasRequestToRemote + && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed( + transition, false, null); + } + return handledToPip; + } else if (mType == TYPE_RECENTS_DURING_SPLIT) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + // Pip auto-entering info might be appended to recent transition like pressing + // home-key in 3-button navigation. This offers split handler the opportunity to + // handle split to pip animation. + if (mPipHandler.isEnteringPip(change, info.getType()) + && mSplitHandler.getSplitItemPosition(change.getLastParent()) + != SPLIT_POSITION_UNDEFINED) { + return animateEnterPipFromSplit( + this, info, startTransaction, finishTransaction, finishCallback, + mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + } + } + + return animateRecentsDuringSplit( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_KEYGUARD) { + return animateKeyguard(this, info, startTransaction, finishTransaction, + finishCallback, mKeyguardHandler, mPipHandler); + } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { + return animateRecentsDuringKeyguard( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { + return animateRecentsDuringDesktop( + info, startTransaction, finishTransaction, finishCallback); + } else if (mType == TYPE_UNFOLD) { + return animateUnfold( + info, startTransaction, finishTransaction, finishCallback); + } else { + throw new IllegalStateException( + "Starting mixed animation without a known mixed type? " + mType); + } + } + + private boolean animateEnterPipFromActivityEmbedding( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + + "entering PIP from an Activity Embedding window"); + // Split into two transitions (wct) + TransitionInfo.Change pipChange = null; + final TransitionInfo everythingElse = + subCopy(info, TRANSIT_TO_BACK, true /* changes */); + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipChange != null) { + throw new IllegalStateException("More than 1 pip-entering changes in one" + + " transition? " + info); + } + pipChange = change; + // going backwards, so remove-by-index is fine. + everythingElse.getChanges().remove(i); + } + } + + final Transitions.TransitionFinishCallback finishCB = (wct) -> { + --mInFlightSubAnimations; + joinFinishArgs(wct); + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(mFinishWCT); + }; + + if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) { + // Fallback to dispatching to other handlers. + return false; + } + + // PIP window should always be on the highest Z order. + if (pipChange != null) { + mInFlightSubAnimations = 2; + mPipHandler.startEnterAnimation( + pipChange, + startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE), + finishTransaction, + finishCB); + } else { + mInFlightSubAnimations = 1; + } + + mActivityEmbeddingController.startAnimation(mTransition, everythingElse, + startTransaction, finishTransaction, finishCB); + return true; + } + + private boolean animateOpenIntentWithRemoteAndPip( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + TransitionInfo.Change pipChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipChange != null) { + throw new IllegalStateException("More than 1 pip-entering changes in one" + + " transition? " + info); + } + pipChange = change; + info.getChanges().remove(i); + } + } + Transitions.TransitionFinishCallback finishCB = (wct) -> { + --mInFlightSubAnimations; + joinFinishArgs(wct); + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(mFinishWCT); + }; + if (pipChange == null) { + if (mLeftoversHandler != null) { + mInFlightSubAnimations = 1; + if (mLeftoversHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB)) { + return true; + } + } + return false; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate" + + " animation because remote-animation likely doesn't support it"); + // Split the transition into 2 parts: the pip part and the rest. + mInFlightSubAnimations = 2; + // make a new startTransaction because pip's startEnterAnimation "consumes" it so + // we need a separate one to send over to launcher. + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + + mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); + + // Dispatch the rest of the transition normally. + if (mLeftoversHandler != null + && mLeftoversHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB)) { + return true; + } + mLeftoversHandler = mPlayer.dispatchTransition(mTransition, info, + startTransaction, finishTransaction, finishCB, mMixedHandler); + return true; + } + + private boolean animateRecentsDuringSplit( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + // Split-screen is only interested in the recents transition finishing (and merging), so + // just wrap finish and start recents animation directly. + Transitions.TransitionFinishCallback finishCB = (wct) -> { + mInFlightSubAnimations = 0; + // If pair-to-pair switching, the post-recents clean-up isn't needed. + wct = wct != null ? wct : new WindowContainerTransaction(); + if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) { + mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction); + } else { + // notify pair-to-pair recents animation finish + mSplitHandler.onRecentsPairToPairAnimationFinish(wct); + } + mSplitHandler.onTransitionAnimationComplete(); + finishCallback.onTransitionFinished(wct); + }; + mInFlightSubAnimations = 1; + mSplitHandler.onRecentsInSplitAnimationStart(info); + final boolean handled = mLeftoversHandler.startAnimation(mTransition, info, + startTransaction, finishTransaction, finishCB); + if (!handled) { + mSplitHandler.onRecentsInSplitAnimationCanceled(); + } + return handled; + } + + private boolean animateRecentsDuringKeyguard( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mInfo == null) { + mInfo = info; + mFinishT = finishTransaction; + mFinishCB = finishCallback; + } + return startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction); + } + + private boolean animateRecentsDuringDesktop( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + Transitions.TransitionFinishCallback finishCB = wct -> { + mInFlightSubAnimations--; + if (mInFlightSubAnimations == 0) { + finishCallback.onTransitionFinished(wct); + } + }; + + mInFlightSubAnimations++; + boolean consumed = mRecentsHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB); + if (!consumed) { + mInFlightSubAnimations--; + return false; + } + if (mDesktopTasksController != null) { + mDesktopTasksController.syncSurfaceState(info, finishTransaction); + return true; + } + + return false; + } + + private boolean animateUnfold( + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + final Transitions.TransitionFinishCallback finishCB = (wct) -> { + mInFlightSubAnimations--; + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(wct); + }; + mInFlightSubAnimations = 1; + // Sync pip state. + if (mPipHandler != null) { + mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); + } + if (mSplitHandler != null && mSplitHandler.isSplitActive()) { + mSplitHandler.updateSurfaces(startTransaction); + } + return mUnfoldHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB); + } + + void mergeAnimation( + @NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { + // queue since no actual animation. + } else if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { + if (mAnimType == ANIM_TYPE_GOING_HOME) { + boolean ended = mSplitHandler.end(); + // If split couldn't end (because it is remote), then don't end everything else + // since we have to play out the animation anyways. + if (!ended) return; + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + } + } else { + mPipHandler.end(); + } + } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + mPipHandler.end(); + mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); + } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); + } + } else if (mType == TYPE_RECENTS_DURING_SPLIT) { + if (mSplitHandler.isPendingEnter(transition)) { + // Recents -> enter-split means that we are switching from one pair to + // another pair. + mAnimType = ANIM_TYPE_PAIR_TO_PAIR; + } + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_KEYGUARD) { + mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { + DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT); + if (animateKeyguard( + this, info, t, mFinishT, mFinishCB, mKeyguardHandler, mPipHandler)) { + finishCallback.onTransitionFinished(null); + } + } + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mType == TYPE_UNFOLD) { + mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else { + throw new IllegalStateException( + "Playing a mixed transition with unknown type? " + mType); + } + } + + void onTransitionConsumed( + @NonNull IBinder transition, boolean aborted, + @Nullable SurfaceControl.Transaction finishT) { + if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_RECENTS_DURING_SPLIT) { + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_KEYGUARD) { + mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mType == TYPE_UNFOLD) { + mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); + } + if (mHasRequestToRemote) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed( + transition, aborted, finishT); + } } boolean startSubAnimation(Transitions.TransitionHandler handler, TransitionInfo info, @@ -235,8 +622,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, throw new IllegalStateException("Unexpected remote transition in" + "pip-enter-from-split request"); } - mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, - transition)); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition)); WindowContainerTransaction out = new WindowContainerTransaction(); mPipHandler.augmentRequest(transition, request, out); @@ -247,7 +634,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mActivityEmbeddingController != null)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request from an Activity Embedding split"); - mActiveTransitions.add(new MixedTransition( + mActiveTransitions.add(createMixedTransition( MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition)); // Postpone transition splitting to later. WindowContainerTransaction out = new WindowContainerTransaction(); @@ -266,7 +653,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (handler == null) { return null; } - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition); mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); @@ -292,7 +679,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mPlayer.getRemoteTransitionHandler(), new WindowContainerTransaction()); } - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); @@ -301,10 +688,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, final WindowContainerTransaction wct = mUnfoldHandler.handleRequest(transition, request); if (wct != null) { - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_UNFOLD, transition); - mixed.mLeftoversHandler = mUnfoldHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_UNFOLD, transition)); } return wct; } @@ -330,31 +715,31 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private void setRecentsTransitionDuringSplit(IBinder transition) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + "Split-Screen is foreground, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition)); } private void setRecentsTransitionDuringKeyguard(IBinder transition) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + "keyguard is visible, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition)); } private void setRecentsTransitionDuringDesktop(IBinder transition) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + "desktop mode is active, so treat it as Mixed."); - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition); - mixed.mLeftoversHandler = mRecentsHandler; - mActiveTransitions.add(mixed); + mActiveTransitions.add(createMixedTransition( + MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition)); + } + + private MixedTransition createMixedTransition(int type, IBinder transition) { + return new MixedTransition(type, transition, mPlayer, this, mPipHandler, mRecentsHandler, + mSplitHandler, mKeyguardHandler, mDesktopTasksController, mUnfoldHandler, + mActivityEmbeddingController); } - private TransitionInfo subCopy(@NonNull TransitionInfo info, + private static TransitionInfo subCopy(@NonNull TransitionInfo info, @WindowManager.TransitionType int newType, boolean withChanges) { final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0); out.setTrack(info.getTrack()); @@ -371,12 +756,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return out; } - private boolean isHomeOpening(@NonNull TransitionInfo.Change change) { + private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) { return change.getTaskInfo() != null && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME; } - private boolean isWallpaper(@NonNull TransitionInfo.Change change) { + private static boolean isWallpaper(@NonNull TransitionInfo.Change change) { return (change.getFlags() & FLAG_IS_WALLPAPER) != 0; } @@ -398,14 +783,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (KeyguardTransitionHandler.handles(info)) { if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) { final MixedTransition keyguardMixed = - new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition); + createMixedTransition(MixedTransition.TYPE_KEYGUARD, transition); mActiveTransitions.add(keyguardMixed); Transitions.TransitionFinishCallback callback = wct -> { mActiveTransitions.remove(keyguardMixed); finishCallback.onTransitionFinished(wct); }; final boolean hasAnimateKeyguard = animateKeyguard( - keyguardMixed, info, startTransaction, finishTransaction, callback); + keyguardMixed, info, startTransaction, finishTransaction, callback, + mKeyguardHandler, mPipHandler); if (hasAnimateKeyguard) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Converting mixed transition into a keyguard transition"); @@ -429,175 +815,21 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, finishCallback.onTransitionFinished(wct); }; - if (chosenTransition.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { - return animateEnterPipFromSplit( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType - == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - return animateEnterPipFromActivityEmbedding( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) { - return false; - } else if (chosenTransition.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - final boolean handledToPip = animateOpenIntentWithRemoteAndPip( - chosenTransition, info, startTransaction, finishTransaction, callback); - // Consume the transition on remote handler if the leftover handler already handle this - // transition. And if it cannot, the transition will be handled by remote handler, so - // don't consume here. - // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip - if (handledToPip && chosenTransition.mHasRequestToRemote - && chosenTransition.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { - mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null); - } - return handledToPip; - } else if (chosenTransition.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - // Pip auto-entering info might be appended to recent transition like pressing - // home-key in 3-button navigation. This offers split handler the opportunity to - // handle split to pip animation. - if (mPipHandler.isEnteringPip(change, info.getType()) - && mSplitHandler.getSplitItemPosition(change.getLastParent()) - != SPLIT_POSITION_UNDEFINED) { - return animateEnterPipFromSplit( - chosenTransition, info, startTransaction, finishTransaction, callback); - } - } - - return animateRecentsDuringSplit( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_KEYGUARD) { - return animateKeyguard( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) { - return animateRecentsDuringKeyguard( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { - return animateRecentsDuringDesktop( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else if (chosenTransition.mType == MixedTransition.TYPE_UNFOLD) { - return animateUnfold( - chosenTransition, info, startTransaction, finishTransaction, callback); - } else { + boolean handled = chosenTransition.startAnimation( + transition, info, startTransaction, finishTransaction, callback); + if (!handled) { mActiveTransitions.remove(chosenTransition); - throw new IllegalStateException("Starting mixed animation without a known mixed type? " - + chosenTransition.mType); - } - } - - private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " - + "entering PIP from an Activity Embedding window"); - // Split into two transitions (wct) - TransitionInfo.Change pipChange = null; - final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */); - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - TransitionInfo.Change change = info.getChanges().get(i); - if (mPipHandler.isEnteringPip(change, info.getType())) { - if (pipChange != null) { - throw new IllegalStateException("More than 1 pip-entering changes in one" - + " transition? " + info); - } - pipChange = change; - // going backwards, so remove-by-index is fine. - everythingElse.getChanges().remove(i); - } - } - - final Transitions.TransitionFinishCallback finishCB = (wct) -> { - --mixed.mInFlightSubAnimations; - mixed.joinFinishArgs(wct); - if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(mixed.mFinishWCT); - }; - - if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) { - // Fallback to dispatching to other handlers. - return false; - } - - // PIP window should always be on the highest Z order. - if (pipChange != null) { - mixed.mInFlightSubAnimations = 2; - mPipHandler.startEnterAnimation( - pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE), - finishTransaction, - finishCB); - } else { - mixed.mInFlightSubAnimations = 1; - } - - mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse, - startTransaction, finishTransaction, finishCB); - return true; - } - - private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - TransitionInfo.Change pipChange = null; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - TransitionInfo.Change change = info.getChanges().get(i); - if (mPipHandler.isEnteringPip(change, info.getType())) { - if (pipChange != null) { - throw new IllegalStateException("More than 1 pip-entering changes in one" - + " transition? " + info); - } - pipChange = change; - info.getChanges().remove(i); - } - } - Transitions.TransitionFinishCallback finishCB = (wct) -> { - --mixed.mInFlightSubAnimations; - mixed.joinFinishArgs(wct); - if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(mixed.mFinishWCT); - }; - if (pipChange == null) { - if (mixed.mLeftoversHandler != null) { - mixed.mInFlightSubAnimations = 1; - if (mixed.mLeftoversHandler.startAnimation(mixed.mTransition, - info, startTransaction, finishTransaction, finishCB)) { - return true; - } - } - mActiveTransitions.remove(mixed); - return false; } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate" - + " animation because remote-animation likely doesn't support it"); - // Split the transition into 2 parts: the pip part and the rest. - mixed.mInFlightSubAnimations = 2; - // make a new startTransaction because pip's startEnterAnimation "consumes" it so - // we need a separate one to send over to launcher. - SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); - - mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); - - // Dispatch the rest of the transition normally. - if (mixed.mLeftoversHandler != null - && mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info, - startTransaction, finishTransaction, finishCB)) { - return true; - } - mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, info, - startTransaction, finishTransaction, finishCB, this); - return true; + return handled; } - private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed, + private static boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { + @NonNull Transitions.TransitionFinishCallback finishCallback, + @NonNull Transitions player, @NonNull DefaultMixedHandler mixedHandler, + @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + "entering PIP while Split-Screen is foreground."); TransitionInfo.Change pipChange = null; @@ -606,7 +838,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, boolean homeIsOpening = false; for (int i = info.getChanges().size() - 1; i >= 0; --i) { TransitionInfo.Change change = info.getChanges().get(i); - if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipHandler.isEnteringPip(change, info.getType())) { if (pipChange != null) { throw new IllegalStateException("More than 1 pip-entering changes in one" + " transition? " + info); @@ -622,7 +854,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } if (pipChange == null) { // um, something probably went wrong. - mActiveTransitions.remove(mixed); return false; } final boolean isGoingHome = homeIsOpening; @@ -630,13 +861,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, --mixed.mInFlightSubAnimations; mixed.joinFinishArgs(wct); if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); if (isGoingHome) { - mSplitHandler.onTransitionAnimationComplete(); + splitHandler.onTransitionAnimationComplete(); } finishCallback.onTransitionFinished(mixed.mFinishWCT); }; - if (isGoingHome || mSplitHandler.getSplitItemPosition(pipChange.getLastParent()) + if (isGoingHome || splitHandler.getSplitItemPosition(pipChange.getLastParent()) != SPLIT_POSITION_UNDEFINED) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed " + "since entering-PiP caused us to leave split and return home."); @@ -652,7 +882,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // we need a separate one to send over to launcher. SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED; - if (mSplitHandler.isSplitScreenVisible()) { + if (splitHandler.isSplitScreenVisible()) { // The non-going home case, we could be pip-ing one of the split stages and keep // showing the other for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -662,7 +892,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, continue; } @SplitScreen.StageType int splitItemStage = - mSplitHandler.getSplitItemStage(change.getLastParent()); + splitHandler.getSplitItemStage(change.getLastParent()); if (splitItemStage != STAGE_TYPE_UNDEFINED) { topStageToKeep = splitItemStage; break; @@ -670,7 +900,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } } // Let split update internal state for dismiss. - mSplitHandler.prepareDismissAnimation(topStageToKeep, + splitHandler.prepareDismissAnimation(topStageToKeep, EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT, finishTransaction); @@ -684,13 +914,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } } - mPipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA); - mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction, + pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA); + pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction, finishCB); // Dispatch the rest of the transition normally. This will most-likely be taken by // recents or default handler. - mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, everythingElse, - otherStartT, finishTransaction, finishCB, this); + mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse, + otherStartT, finishTransaction, finishCB, mixedHandler); } else { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just " + "forward animation to Pip-Handler."); @@ -698,7 +928,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // new pip task is spawned). In this case, we don't actually exit split so we can // just let pip transition handle the animation verbatim. mixed.mInFlightSubAnimations = 1; - mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction, + pipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction, finishCB); } return true; @@ -735,14 +965,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCallback) { - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition); mActiveTransitions.add(mixed); Transitions.TransitionFinishCallback callback = wct -> { mActiveTransitions.remove(mixed); finishCallback.onTransitionFinished(wct); }; - return animateEnterPipFromSplit(mixed, info, startT, finishT, callback); + return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback, mPlayer, this, + mPipHandler, mSplitHandler); } /** @@ -765,7 +996,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } if (displayPart.getChanges().isEmpty()) return false; unlinkMissingParents(everythingElse); - final MixedTransition mixed = new MixedTransition( + final MixedTransition mixed = createMixedTransition( MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE, transition); mActiveTransitions.add(mixed); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is a mix of display change " @@ -794,116 +1025,22 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return true; } - private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - // Split-screen is only interested in the recents transition finishing (and merging), so - // just wrap finish and start recents animation directly. - Transitions.TransitionFinishCallback finishCB = (wct) -> { - mixed.mInFlightSubAnimations = 0; - mActiveTransitions.remove(mixed); - // If pair-to-pair switching, the post-recents clean-up isn't needed. - wct = wct != null ? wct : new WindowContainerTransaction(); - if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) { - mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction); - } else { - // notify pair-to-pair recents animation finish - mSplitHandler.onRecentsPairToPairAnimationFinish(wct); - } - mSplitHandler.onTransitionAnimationComplete(); - finishCallback.onTransitionFinished(wct); - }; - mixed.mInFlightSubAnimations = 1; - mSplitHandler.onRecentsInSplitAnimationStart(info); - final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info, - startTransaction, finishTransaction, finishCB); - if (!handled) { - mSplitHandler.onRecentsInSplitAnimationCanceled(); - mActiveTransitions.remove(mixed); - } - return handled; - } - - private boolean animateKeyguard(@NonNull final MixedTransition mixed, + private static boolean animateKeyguard(@NonNull final MixedTransition mixed, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { + @NonNull Transitions.TransitionFinishCallback finishCallback, + @NonNull KeyguardTransitionHandler keyguardHandler, + PipTransitionController pipHandler) { if (mixed.mFinishT == null) { mixed.mFinishT = finishTransaction; mixed.mFinishCB = finishCallback; } // Sync pip state. - if (mPipHandler != null) { - mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); - } - return mixed.startSubAnimation(mKeyguardHandler, info, startTransaction, finishTransaction); - } - - private boolean animateRecentsDuringKeyguard(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (mixed.mInfo == null) { - mixed.mInfo = info; - mixed.mFinishT = finishTransaction; - mixed.mFinishCB = finishCallback; - } - return mixed.startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction); - } - - private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - Transitions.TransitionFinishCallback finishCB = wct -> { - mixed.mInFlightSubAnimations--; - if (mixed.mInFlightSubAnimations == 0) { - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(wct); - } - }; - - mixed.mInFlightSubAnimations++; - boolean consumed = mRecentsHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCB); - if (!consumed) { - mixed.mInFlightSubAnimations--; - return false; - } - if (mDesktopTasksController != null) { - mDesktopTasksController.syncSurfaceState(info, finishTransaction); - return true; - } - - return false; - } - - private boolean animateUnfold(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - final Transitions.TransitionFinishCallback finishCB = (wct) -> { - mixed.mInFlightSubAnimations--; - if (mixed.mInFlightSubAnimations > 0) return; - mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(wct); - }; - mixed.mInFlightSubAnimations = 1; - // Sync pip state. - if (mPipHandler != null) { - mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); - } - if (mSplitHandler != null && mSplitHandler.isSplitActive()) { - mSplitHandler.updateSurfaces(startTransaction); + if (pipHandler != null) { + pipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); } - return mUnfoldHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCB); + return mixed.startSubAnimation(keyguardHandler, info, startTransaction, finishTransaction); } /** Use to when split use intent to enter, check if this enter transition should be mixed or @@ -947,65 +1084,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull Transitions.TransitionFinishCallback finishCallback) { for (int i = 0; i < mActiveTransitions.size(); ++i) { if (mActiveTransitions.get(i).mTransition != mergeTarget) continue; + MixedTransition mixed = mActiveTransitions.get(i); if (mixed.mInFlightSubAnimations <= 0) { // Already done, so no need to end it. return; } - if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) { - // queue since no actual animation. - } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { - if (mixed.mAnimType == MixedTransition.ANIM_TYPE_GOING_HOME) { - boolean ended = mSplitHandler.end(); - // If split couldn't end (because it is remote), then don't end everything else - // since we have to play out the animation anyways. - if (!ended) return; - mPipHandler.end(); - if (mixed.mLeftoversHandler != null) { - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } - } else { - mPipHandler.end(); - } - } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.end(); - mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mPipHandler.end(); - if (mixed.mLeftoversHandler != null) { - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - if (mSplitHandler.isPendingEnter(transition)) { - // Recents -> enter-split means that we are switching from one pair to - // another pair. - mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR; - } - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { - mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) { - if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { - handoverTransitionLeashes(mixed, info, t, mixed.mFinishT); - if (animateKeyguard(mixed, info, t, mixed.mFinishT, mixed.mFinishCB)) { - finishCallback.onTransitionFinished(null); - } - } - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { - mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else { - throw new IllegalStateException("Playing a mixed transition with unknown type? " - + mixed.mType); - } + mixed.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } } @@ -1018,46 +1103,30 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed = mActiveTransitions.remove(i); break; } - if (mixed == null) return; - if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { - mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { - mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { - mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); - } - if (mixed.mHasRequestToRemote) { - mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT); + if (mixed != null) { + mixed.onTransitionConsumed(transition, aborted, finishT); } } /** - * Update an incoming {@link TransitionInfo} with the leashes from an ongoing - * {@link MixedTransition} so that it can take over some parts of the animation without + * Update an incoming {@link TransitionInfo} with the leashes from an existing + * {@link TransitionInfo} so that it can take over some parts of the animation without * reparenting to new transition roots. */ - private static void handoverTransitionLeashes(@NonNull MixedTransition mixed, - @NonNull TransitionInfo info, + private static void handoverTransitionLeashes( + @NonNull TransitionInfo from, + @NonNull TransitionInfo to, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT) { // Show the roots in case they contain new changes not present in the original transition. - for (int j = info.getRootCount() - 1; j >= 0; --j) { - startT.show(info.getRoot(j).getLeash()); + for (int j = to.getRootCount() - 1; j >= 0; --j) { + startT.show(to.getRoot(j).getLeash()); } // Find all of the leashes from the original transition. Map<WindowContainerToken, TransitionInfo.Change> originalChanges = new ArrayMap<>(); - for (TransitionInfo.Change oldChange : mixed.mInfo.getChanges()) { + for (TransitionInfo.Change oldChange : from.getChanges()) { if (oldChange.getContainer() != null) { originalChanges.put(oldChange.getContainer(), oldChange); } @@ -1065,9 +1134,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // Merge the animation leashes by re-using the original ones if we see the same container // in the new transition and the old. - for (TransitionInfo.Change newChange : info.getChanges()) { + for (TransitionInfo.Change newChange : to.getChanges()) { if (originalChanges.containsKey(newChange.getContainer())) { - final TransitionInfo.Change oldChange = originalChanges.get(newChange.getContainer()); + final TransitionInfo.Change oldChange = originalChanges.get( + newChange.getContainer()); startT.reparent(newChange.getLeash(), null); newChange.setLeash(oldChange.getLeash()); } diff --git a/libs/WindowManager/Shell/tests/flicker/pip/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 182a9089d040..be771712834f 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/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 @@ -101,7 +101,8 @@ abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTran override fun pipLayerReduces() { flicker.assertLayers { val pipLayerList = - this.layers { standardAppHelper.layerMatchesAnyOf(it) && it.isVisible } + this.layers { standardAppHelper.packageNameMatcher.layerMatchesAnyOf(it) + && it.isVisible } pipLayerList.zipWithNext { previous, current -> current.visibleRegion.notBiggerThan(previous.visibleRegion.region) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java deleted file mode 100644 index 6ebee730756e..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java +++ /dev/null @@ -1,602 +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. - */ - -package com.android.wm.shell.bubbles; - -import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; - -import static org.mockito.Mockito.mock; - -import android.content.Intent; -import android.content.pm.ShortcutInfo; -import android.graphics.Insets; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.os.UserHandle; -import android.testing.AndroidTestingRunner; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests operations and the resulting state managed by {@link BubblePositioner}. - */ -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class BubblePositionerTest extends ShellTestCase { - - private BubblePositioner mPositioner; - - @Before - public void setUp() { - WindowManager windowManager = mContext.getSystemService(WindowManager.class); - mPositioner = new BubblePositioner(mContext, windowManager); - } - - @Test - public void testUpdate() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1000, 1200); - Rect availableRect = new Rect(screenBounds); - availableRect.inset(insets); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect); - assertThat(mPositioner.isLandscape()).isFalse(); - assertThat(mPositioner.isLargeScreen()).isFalse(); - assertThat(mPositioner.getInsets()).isEqualTo(insets); - } - - @Test - public void testShowBubblesVertically_phonePortrait() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isFalse(); - } - - @Test - public void testShowBubblesVertically_phoneLandscape() { - DeviceConfig deviceConfig = new ConfigBuilder().setLandscape().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.isLandscape()).isTrue(); - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - @Test - public void testShowBubblesVertically_tablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - /** If a resting position hasn't been set, calling it will return the default position. */ - @Test - public void testGetRestingPosition_returnsDefaultPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = mPositioner.getRestingPosition(); - PointF defaultPosition = mPositioner.getDefaultStartPosition(); - - assertThat(restingPosition).isEqualTo(defaultPosition); - } - - /** If a resting position has been set, it'll return that instead of the default position. */ - @Test - public void testGetRestingPosition_returnsRestingPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = new PointF(100, 100); - mPositioner.setRestingPosition(restingPosition); - - assertThat(mPositioner.getRestingPosition()).isEqualTo(restingPosition); - } - - /** Test that the default resting position on phone is in upper left. */ - @Test - public void testGetRestingPosition_bubble_onPhone() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_bubble_onPhone_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle left. */ - @Test - public void testGetRestingPosition_chatBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_chatBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle right. */ - @Test - public void testGetDefaultPosition_appBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_appBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testHasUserModifiedDefaultPosition_false() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition()); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - } - - @Test - public void testHasUserModifiedDefaultPosition_true() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(new PointF(0, 100)); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue(); - } - - @Test - public void testGetExpandedViewHeight_max() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT); - } - - @Test - public void testGetExpandedViewHeight_customHeight_valid() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - minHeight + 100 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the desired value - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo( - bubble.getDesiredHeight(mContext)); - } - - - @Test - public void testGetExpandedViewHeight_customHeight_tooSmall() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - 10 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the minimum value - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight); - } - - @Test - public void testGetMaxExpandedViewHeight_onLargeTablet() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - int expandedViewPadding = mContext.getResources().getDimensionPixelSize(R - .dimen.bubble_expanded_view_padding); - float expectedHeight = 1800 - 2 * 20 - manageButtonHeight - pointerWidth - - expandedViewPadding * 2; - assertThat(((float) mPositioner.getMaxExpandedViewHeight(false /* isOverflow */))) - .isWithin(0.1f).of(expectedHeight); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_true() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isTrue(); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_smallTablet_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_phone_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testExpandedViewY_phoneLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height so it'll always be top aligned - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_phonePortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // Always top aligned in phone portrait - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on landscape, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int manageButtonPlusMargin = manageButtonHeight + 2 - * mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_manage_button_margin); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - - final float expectedExpandedViewY = mPositioner.getAvailableRect().bottom - - manageButtonPlusMargin - - mPositioner.getExpandedViewHeightForLargeScreen() - - pointerWidth; - - // Bubbles are bottom aligned on portrait, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(expectedExpandedViewY); - } - - /** - * Calculates the Y position bubbles should be placed based on the config. Based on - * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and - * {@link BubbleStackView.RelativeStackPosition}. - */ - private float getDefaultYPosition() { - final boolean isTablet = mPositioner.isLargeScreen(); - - // On tablet the position is centered, on phone it is an offset from the top. - final float desiredY = isTablet - ? mPositioner.getScreenRect().height() / 2f - (mPositioner.getBubbleSize() / 2f) - : mContext.getResources().getDimensionPixelOffset( - R.dimen.bubble_stack_starting_offset_y); - // Since we're visually centering the bubbles on tablet, use total screen height rather - // than the available height. - final float height = isTablet - ? mPositioner.getScreenRect().height() - : mPositioner.getAvailableRect().height(); - float offsetPercent = desiredY / height; - offsetPercent = Math.max(0f, Math.min(1f, offsetPercent)); - final RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent; - } - - /** - * Sets up window manager to return config values based on what you need for the test. - * By default it sets up a portrait phone without any insets. - */ - private static class ConfigBuilder { - private Rect mScreenBounds = new Rect(0, 0, 1000, 2000); - private boolean mIsLargeScreen = false; - private boolean mIsSmallTablet = false; - private boolean mIsLandscape = false; - private boolean mIsRtl = false; - private Insets mInsets = Insets.of(0, 0, 0, 0); - - public ConfigBuilder setScreenBounds(Rect screenBounds) { - mScreenBounds = screenBounds; - return this; - } - - public ConfigBuilder setLargeScreen() { - mIsLargeScreen = true; - return this; - } - - public ConfigBuilder setSmallTablet() { - mIsSmallTablet = true; - return this; - } - - public ConfigBuilder setLandscape() { - mIsLandscape = true; - return this; - } - - public ConfigBuilder setRtl() { - mIsRtl = true; - return this; - } - - public ConfigBuilder setInsets(Insets insets) { - mInsets = insets; - return this; - } - - private DeviceConfig build() { - return new DeviceConfig(mIsLargeScreen, mIsSmallTablet, mIsLandscape, mIsRtl, - mScreenBounds, mInsets); - } - } -} diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 2f28363aedc7..77800a305f02 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -31,6 +31,12 @@ license { ], } +cc_aconfig_library { + name: "backup_flags_cc_lib", + host_supported: true, + aconfig_declarations: "backup_flags", +} + cc_defaults { name: "libandroidfw_defaults", cpp_std: "gnu++2b", @@ -115,7 +121,10 @@ cc_library { "libutils", "libz", ], - static_libs: ["libziparchive_for_incfs"], + static_libs: [ + "libziparchive_for_incfs", + "backup_flags_cc_lib", + ], static: { enabled: false, }, diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp index 1a6a952492f6..a1e7c2ffc1b1 100644 --- a/libs/androidfw/BackupHelpers.cpp +++ b/libs/androidfw/BackupHelpers.cpp @@ -36,6 +36,9 @@ #include <utils/KeyedVector.h> #include <utils/String8.h> +#include <com_android_server_backup.h> +namespace backup_flags = com::android::server::backup; + namespace android { #define MAGIC0 0x70616e53 // Snap @@ -214,7 +217,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& { LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.c_str(), mode); - const int bufsize = 4*1024; + const int bufsize = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (4*1024); int err; int amt; int fileSize; @@ -550,7 +553,8 @@ int write_tarfile(const String8& packageName, const String8& domain, } // read/write up to this much at a time. - const size_t BUFSIZE = 32 * 1024; + const size_t BUFSIZE = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (32*1024); + char* buf = (char *)calloc(1,BUFSIZE); const size_t PAXHEADER_OFFSET = 512; const size_t PAXHEADER_SIZE = 512; @@ -726,7 +730,7 @@ done: -#define RESTORE_BUF_SIZE (8*1024) +const size_t RESTORE_BUF_SIZE = backup_flags::enable_max_size_writes_to_pipes() ? 64*1024 : 8*1024; RestoreHelperBase::RestoreHelperBase() { diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 6c3172a26751..d58c872dbc56 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -56,6 +56,7 @@ std::optional<std::int32_t> render_ahead() { bool Properties::debugLayersUpdates = false; bool Properties::debugOverdraw = false; +bool Properties::debugTraceGpuResourceCategories = false; bool Properties::showDirtyRegions = false; bool Properties::skipEmptyFrames = true; bool Properties::useBufferAge = true; @@ -151,10 +152,12 @@ bool Properties::load() { skpCaptureEnabled = debuggingEnabled && base::GetBoolProperty(PROPERTY_CAPTURE_SKP_ENABLED, false); - SkAndroidFrameworkTraceUtil::setEnableTracing( - base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false)); + bool skiaBroadTracing = base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false); + SkAndroidFrameworkTraceUtil::setEnableTracing(skiaBroadTracing); SkAndroidFrameworkTraceUtil::setUsePerfettoTrackEvents( base::GetBoolProperty(PROPERTY_SKIA_USE_PERFETTO_TRACK_EVENTS, false)); + debugTraceGpuResourceCategories = + base::GetBoolProperty(PROPERTY_TRACE_GPU_RESOURCES, skiaBroadTracing); runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index bca57e9e4678..b956facf6f90 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -143,6 +143,15 @@ enum DebugLevel { #define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled" /** + * Might split Skia's GPU resource utilization into separate tracing tracks (slow). + * + * Aggregate total and purgeable numbers will still be reported under a "misc" track when this is + * disabled, they just won't be split into distinct categories. Results may vary depending on GPU + * backend/API, and the category mappings defined in ATraceMemoryDump's hardcoded sResourceMap. + */ +#define PROPERTY_TRACE_GPU_RESOURCES "debug.hwui.trace_gpu_resources" + +/** * Allows broad recording of Skia drawing commands. * * If disabled, a very minimal set of trace events *may* be recorded. @@ -254,6 +263,7 @@ public: static bool debugLayersUpdates; static bool debugOverdraw; + static bool debugTraceGpuResourceCategories; static bool showDirtyRegions; // TODO: Remove after stabilization period static bool skipEmptyFrames; diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig index c156c46a5a9b..72ddeccd26b2 100644 --- a/libs/hwui/aconfig/hwui_flags.aconfig +++ b/libs/hwui/aconfig/hwui_flags.aconfig @@ -22,6 +22,13 @@ flag { } flag { + name: "high_contrast_text_small_text_rect" + namespace: "accessibility" + description: "Draw a solid rectangle background behind text instead of a stroke outline" + bug: "186567103" +} + +flag { name: "hdr_10bit_plus" namespace: "core_graphics" description: "Use 10101010 and FP16 formats for HDR-UI when available" diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 80b6c0385fca..e9f4b81c7624 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -18,6 +18,7 @@ #include <SkFontMetrics.h> #include <SkRRect.h> +#include <minikin/MinikinRect.h> #include "FeatureFlags.h" #include "MinikinUtils.h" @@ -107,7 +108,13 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, // care of all alignment. paint.setTextAlign(Paint::kLeft_Align); - DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance()); + minikin::MinikinRect bounds; + // We only need the bounds to draw a rectangular background in high contrast mode. Let's save + // the cycles otherwise. + if (flags::high_contrast_text_small_text_rect() && isHighContrastText()) { + MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, textSize, &bounds); + } + DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance(), bounds); MinikinUtils::forFontRun(layout, &paint, f); if (text_feature::fix_double_underline()) { diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h index 8f999904a8ab..ba6543988a7b 100644 --- a/libs/hwui/hwui/DrawTextFunctor.h +++ b/libs/hwui/hwui/DrawTextFunctor.h @@ -33,6 +33,8 @@ namespace flags = com::android::graphics::hwui::flags; namespace android { +inline constexpr int kHighContrastTextBorderWidth = 4; + static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, const Paint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); @@ -45,15 +47,26 @@ static void simplifyPaint(int color, Paint* paint) { paint->setShader(nullptr); paint->setColorFilter(nullptr); paint->setLooper(nullptr); - paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); + paint->setStrokeWidth(kHighContrastTextBorderWidth + 0.04 * paint->getSkFont().getSize()); paint->setStrokeJoin(SkPaint::kRound_Join); paint->setLooper(nullptr); } class DrawTextFunctor { public: + /** + * Creates a Functor to draw the given text layout. + * + * @param layout + * @param canvas + * @param paint + * @param x + * @param y + * @param totalAdvance + * @param bounds bounds of the text. Only required if high contrast text mode is enabled. + */ DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x, - float y, float totalAdvance) + float y, float totalAdvance, const minikin::MinikinRect& bounds) : layout(layout) , canvas(canvas) , paint(paint) @@ -61,7 +74,8 @@ public: , y(y) , totalAdvance(totalAdvance) , underlinePosition(0) - , underlineThickness(0) {} + , underlineThickness(0) + , bounds(bounds) {} void operator()(size_t start, size_t end) { auto glyphFunc = [&](uint16_t* text, float* positions) { @@ -91,7 +105,16 @@ public: Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); - canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); + if (flags::high_contrast_text_small_text_rect()) { + auto bgBounds(bounds); + auto padding = kHighContrastTextBorderWidth + 0.1f * paint.getSkFont().getSize(); + bgBounds.offset(x, y); + canvas->drawRect(bgBounds.mLeft - padding, bgBounds.mTop - padding, + bgBounds.mRight + padding, bgBounds.mBottom + padding, + outlinePaint); + } else { + canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); + } // inner gDrawTextBlobMode = DrawTextBlobMode::HctInner; @@ -146,6 +169,7 @@ private: float totalAdvance; float underlinePosition; float underlineThickness; + const minikin::MinikinRect& bounds; }; } // namespace android diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 7552b56d2ad6..833069f363c8 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -96,7 +96,7 @@ void MinikinUtils::getBounds(const Paint* paint, minikin::Bidi bidiFlags, const float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, float* advances, - minikin::MinikinRect* bounds) { + minikin::MinikinRect* bounds, uint32_t* clusterCount) { minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); const minikin::U16StringPiece textBuf(buf, bufSize); const minikin::Range range(start, start + count); @@ -104,7 +104,7 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit(); return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen, - endHyphen, advances, bounds); + endHyphen, advances, bounds, clusterCount); } minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags, diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index 61bc881faa54..f8574ee50525 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -53,7 +53,7 @@ public: static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, - float* advances, minikin::MinikinRect* bounds); + float* advances, minikin::MinikinRect* bounds, uint32_t* clusterCount); static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 7cc48661619a..8315c4c0dd4d 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -247,6 +247,9 @@ static jfieldID gFontMetricsInt_descent; static jfieldID gFontMetricsInt_bottom; static jfieldID gFontMetricsInt_leading; +static jclass gRunInfo_class; +static jfieldID gRunInfo_clusterCount; + /////////////////////////////////////////////////////////////////////////////// void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) @@ -511,6 +514,10 @@ int GraphicsJNI::set_metrics_int(JNIEnv* env, jobject metrics, const SkFontMetri return descent - ascent + leading; } +void GraphicsJNI::set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount) { + env->SetIntField(runInfo, gRunInfo_clusterCount, clusterCount); +} + /////////////////////////////////////////////////////////////////////////////////////////// jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoderWrapper* bitmap) { @@ -834,5 +841,10 @@ int register_android_graphics_Graphics(JNIEnv* env) gFontMetricsInt_bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I"); gFontMetricsInt_leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I"); + gRunInfo_class = FindClassOrDie(env, "android/graphics/Paint$RunInfo"); + gRunInfo_class = MakeGlobalRefOrDie(env, gRunInfo_class); + + gRunInfo_clusterCount = GetFieldIDOrDie(env, gRunInfo_class, "mClusterCount", "I"); + return 0; } diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index b9fff36d372e..b0a1074d6693 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -77,6 +77,8 @@ public: static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*); static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf); + static void set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount); + static void set_jpoint(JNIEnv*, jobject jrect, int x, int y); static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point); diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index d84b73d1a1ca..58d9d8b9def3 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -114,7 +114,7 @@ namespace PaintGlue { std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0, - count, count, advancesArray.get(), nullptr); + count, count, advancesArray.get(), nullptr, nullptr); for (int i = 0; i < count; i++) { // traverse in the given direction @@ -206,7 +206,7 @@ namespace PaintGlue { } const float advance = MinikinUtils::measureText( paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, - contextCount, advancesArray.get(), nullptr); + contextCount, advancesArray.get(), nullptr, nullptr); if (advances) { env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get()); } @@ -244,7 +244,7 @@ namespace PaintGlue { minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count, - advancesArray.get(), nullptr); + advancesArray.get(), nullptr, nullptr); size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text, start, count, offset, moveOpt); return static_cast<jint>(result); @@ -508,7 +508,7 @@ namespace PaintGlue { static jfloat doRunAdvance(JNIEnv* env, const Paint* paint, const Typeface* typeface, const jchar buf[], jint start, jint count, jint bufSize, jboolean isRtl, jint offset, jfloatArray advances, - jint advancesIndex, SkRect* drawBounds) { + jint advancesIndex, SkRect* drawBounds, uint32_t* clusterCount) { if (advances) { size_t advancesLength = env->GetArrayLength(advances); if ((size_t)(count + advancesIndex) > advancesLength) { @@ -519,9 +519,9 @@ namespace PaintGlue { minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; minikin::MinikinRect bounds; if (offset == start + count && advances == nullptr) { - float result = - MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, - bufSize, nullptr, drawBounds ? &bounds : nullptr); + float result = MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, + bufSize, nullptr, + drawBounds ? &bounds : nullptr, clusterCount); if (drawBounds) { copyMinikinRectToSkRect(bounds, drawBounds); } @@ -529,7 +529,8 @@ namespace PaintGlue { } std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, - advancesArray.get(), drawBounds ? &bounds : nullptr); + advancesArray.get(), drawBounds ? &bounds : nullptr, + clusterCount); if (drawBounds) { copyMinikinRectToSkRect(bounds, drawBounds); @@ -549,7 +550,7 @@ namespace PaintGlue { ScopedCharArrayRO textArray(env, text); jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, - isRtl, offset - contextStart, nullptr, 0, nullptr); + isRtl, offset - contextStart, nullptr, 0, nullptr, nullptr); return result; } @@ -558,27 +559,41 @@ namespace PaintGlue { jint contextStart, jint contextEnd, jboolean isRtl, jint offset, jfloatArray advances, jint advancesIndex, - jobject drawBounds) { + jobject drawBounds, jobject runInfo) { const Paint* paint = reinterpret_cast<Paint*>(paintHandle); const Typeface* typeface = paint->getAndroidTypeface(); ScopedCharArrayRO textArray(env, text); SkRect skDrawBounds; + uint32_t clusterCount = 0; jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, isRtl, offset - contextStart, advances, advancesIndex, - drawBounds ? &skDrawBounds : nullptr); + drawBounds ? &skDrawBounds : nullptr, &clusterCount); if (drawBounds != nullptr) { GraphicsJNI::rect_to_jrectf(skDrawBounds, env, drawBounds); } + if (runInfo) { + GraphicsJNI::set_cluster_count_to_run_info(env, runInfo, clusterCount); + } return result; } + // This method is kept for old Robolectric JNI signature used by SystemUIGoogleRoboRNGTests. + static jfloat getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric( + JNIEnv* env, jclass cls, jlong paintHandle, jcharArray text, jint start, jint end, + jint contextStart, jint contextEnd, jboolean isRtl, jint offset, jfloatArray advances, + jint advancesIndex, jobject drawBounds) { + return getRunCharacterAdvance___CIIIIZI_FI_F(env, cls, paintHandle, text, start, end, + contextStart, contextEnd, isRtl, offset, + advances, advancesIndex, drawBounds, nullptr); + } + static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[], jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) { minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, - advancesArray.get(), nullptr); + advancesArray.get(), nullptr, nullptr); return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance); } @@ -1145,8 +1160,11 @@ static const JNINativeMethod methods[] = { (void*)PaintGlue::getCharArrayBounds}, {"nHasGlyph", "(JILjava/lang/String;)Z", (void*)PaintGlue::hasGlyph}, {"nGetRunAdvance", "(J[CIIIIZI)F", (void*)PaintGlue::getRunAdvance___CIIIIZI_F}, - {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F", + {"nGetRunCharacterAdvance", + "(J[CIIIIZI[FILandroid/graphics/RectF;Landroid/graphics/Paint$RunInfo;)F", (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F}, + {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F", + (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric}, {"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I}, {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V", (void*)PaintGlue::getFontMetricsIntForText___C}, diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp index 234f42d79cb7..756b937f7de3 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -20,6 +20,8 @@ #include <cstring> +#include "GrDirectContext.h" + namespace android { namespace uirenderer { namespace skiapipeline { @@ -114,8 +116,16 @@ void ATraceMemoryDump::startFrame() { /** * logTraces reads from mCurrentValues and logs the counters with ATRACE. + * + * gpuMemoryIsAlreadyInDump must be true if GrDirectContext::dumpMemoryStatistics(...) was called + * with this tracer, false otherwise. Leaving this false allows this function to quickly query total + * and purgable GPU memory without the caller having to spend time in + * GrDirectContext::dumpMemoryStatistics(...) first, which iterates over every resource in the GPU + * cache. This can save significant time, but buckets all GPU memory into a single "misc" track, + * which may be a loss of granularity depending on the GPU backend and the categories defined in + * sResourceMap. */ -void ATraceMemoryDump::logTraces() { +void ATraceMemoryDump::logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext) { // Accumulate data from last dumpName recordAndResetCountersIfNeeded(""); uint64_t hwui_all_frame_memory = 0; @@ -126,6 +136,20 @@ void ATraceMemoryDump::logTraces() { ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory); } } + + if (!gpuMemoryIsAlreadyInDump && grContext) { + // Total GPU memory + int gpuResourceCount; + size_t gpuResourceBytes; + grContext->getResourceCacheUsage(&gpuResourceCount, &gpuResourceBytes); + hwui_all_frame_memory += (uint64_t)gpuResourceBytes; + ATRACE_INT64("HWUI Misc Memory", gpuResourceBytes); + + // Purgable subset of GPU memory + size_t purgeableGpuResourceBytes = grContext->getResourceCachePurgeableBytes(); + ATRACE_INT64("Purgeable HWUI Misc Memory", purgeableGpuResourceBytes); + } + ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory); } diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h index 4592711dd5b5..777d1a2ddb5b 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -16,6 +16,7 @@ #pragma once +#include <GrDirectContext.h> #include <SkString.h> #include <SkTraceMemoryDump.h> @@ -50,7 +51,7 @@ public: void startFrame(); - void logTraces(); + void logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext); private: std::string mLastDumpName; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 30d461271c89..eb4d4948dc53 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -269,13 +269,14 @@ void CacheManager::onFrameCompleted() { cancelDestroyContext(); mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC); if (ATRACE_ENABLED()) { + ATRACE_NAME("dumpingMemoryStatistics"); static skiapipeline::ATraceMemoryDump tracer; tracer.startFrame(); SkGraphics::DumpMemoryStatistics(&tracer); - if (mGrContext) { + if (mGrContext && Properties::debugTraceGpuResourceCategories) { mGrContext->dumpMemoryStatistics(&tracer); } - tracer.logTraces(); + tracer.logTraces(Properties::debugTraceGpuResourceCategories, mGrContext.get()); } } diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp index c70a30477ecf..9911bfa70443 100644 --- a/libs/hwui/tests/unit/UnderlineTest.cpp +++ b/libs/hwui/tests/unit/UnderlineTest.cpp @@ -103,8 +103,9 @@ DrawTextFunctor processFunctor(const std::vector<uint16_t>& text, Paint* paint) // Create minikin::Layout std::unique_ptr<Typeface> typeface(makeTypeface()); minikin::Layout layout = doLayout(text, *paint, typeface.get()); + minikin::MinikinRect bounds; - DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance()); + DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance(), bounds); MinikinUtils::forFontRun(layout, paint, f); return f; } diff --git a/lint-baseline.xml b/lint-baseline.xml index 79b21551a76e..660884a18010 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -433,7 +433,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;" + errorLine1=" boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -444,7 +444,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;" + errorLine1=" boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -455,7 +455,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;" + errorLine1=" boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -466,7 +466,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;" + errorLine1=" boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -562,4 +562,4 @@ column="74"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index ee2510ff9695..0d5af50d08b5 100644 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -19,6 +19,7 @@ package com.android.internal.location; import android.Manifest; import android.annotation.RequiresPermission; import android.content.Context; +import android.content.pm.PackageManager; import android.location.LocationManager; import android.os.SystemClock; import android.telephony.TelephonyCallback; @@ -26,6 +27,8 @@ import android.telephony.TelephonyManager; import android.telephony.emergency.EmergencyNumber; import android.util.Log; +import com.android.internal.telephony.flags.Flags; + import java.util.concurrent.TimeUnit; /** @@ -139,8 +142,20 @@ public class GpsNetInitiatedHandler { (mCallEndElapsedRealtimeMillis > 0) && ((SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis) < emergencyExtensionMillis); - boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode(); - boolean isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + boolean isInEmergencyCallback = false; + boolean isInEmergencySmsMode = false; + if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) { + isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode(); + isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + } else { + PackageManager pm = mContext.getPackageManager(); + if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) { + isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode(); + } + if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) { + isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + } + } return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension || isInEmergencySmsMode; } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 89792c7638cb..8f8cd0b44220 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -48,6 +48,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -141,7 +142,9 @@ public final class MediaRouter2 { * dispatch. This is only used to determine what callback a route should be assigned to (added, * removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}. */ - private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>(); + private volatile ArrayMap<String, MediaRoute2Info> mPreviousFilteredRoutes = new ArrayMap<>(); + + private final Map<String, MediaRoute2Info> mPreviousUnfilteredRoutes = new ArrayMap<>(); /** * Stores the latest copy of exposed routes after filtering, sorting, and deduplication. Can be @@ -282,6 +285,8 @@ public final class MediaRouter2 { MediaRouter2 instance = sAppToProxyRouterMap.get(key); if (instance == null) { instance = new MediaRouter2(context, looper, clientPackageName, user); + // Register proxy router after instantiation to avoid race condition. + ((ProxyMediaRouter2Impl) instance.mImpl).registerProxyRouter(); sAppToProxyRouterMap.put(key, instance); } return instance; @@ -368,6 +373,7 @@ public final class MediaRouter2 { new SystemRoutingController( ProxyMediaRouter2Impl.getSystemSessionInfoImpl( mMediaRouterService, clientPackageName)); + mImpl = new ProxyMediaRouter2Impl(context, clientPackageName, user); } @@ -883,7 +889,7 @@ public final class MediaRouter2 { newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet()); for (MediaRoute2Info route : newRoutes) { - MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId()); + MediaRoute2Info prevRoute = mPreviousFilteredRoutes.get(route.getId()); if (prevRoute == null) { addedRoutes.add(route); } else if (!prevRoute.equals(route)) { @@ -891,21 +897,21 @@ public final class MediaRouter2 { } } - for (int i = 0; i < mPreviousRoutes.size(); i++) { - if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) { - removedRoutes.add(mPreviousRoutes.valueAt(i)); + for (int i = 0; i < mPreviousFilteredRoutes.size(); i++) { + if (!newRouteIds.contains(mPreviousFilteredRoutes.keyAt(i))) { + removedRoutes.add(mPreviousFilteredRoutes.valueAt(i)); } } // update previous routes for (MediaRoute2Info route : removedRoutes) { - mPreviousRoutes.remove(route.getId()); + mPreviousFilteredRoutes.remove(route.getId()); } for (MediaRoute2Info route : addedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } for (MediaRoute2Info route : changedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } if (!addedRoutes.isEmpty()) { @@ -924,6 +930,27 @@ public final class MediaRouter2 { } } + void dispatchControllerUpdatedIfNeededOnHandler(Map<String, MediaRoute2Info> routesMap) { + List<RoutingController> controllers = getControllers(); + for (RoutingController controller : controllers) { + + for (String selectedRoute : controller.getRoutingSessionInfo().getSelectedRoutes()) { + if (routesMap.containsKey(selectedRoute) + && mPreviousUnfilteredRoutes.containsKey(selectedRoute)) { + MediaRoute2Info currentRoute = routesMap.get(selectedRoute); + MediaRoute2Info oldRoute = mPreviousUnfilteredRoutes.get(selectedRoute); + if (!currentRoute.equals(oldRoute)) { + notifyControllerUpdated(controller); + break; + } + } + } + } + + mPreviousUnfilteredRoutes.clear(); + mPreviousUnfilteredRoutes.putAll(routesMap); + } + void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) { synchronized (mLock) { mRoutes.clear(); @@ -945,6 +972,11 @@ public final class MediaRouter2 { MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler, this, mFilteredRoutes)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::dispatchControllerUpdatedIfNeededOnHandler, + this, + new HashMap<>(mRoutes))); } /** @@ -2153,18 +2185,19 @@ public final class MediaRouter2 { mClientUser = user; mClientPackageName = clientPackageName; mClient = new Client(); + mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; + } + public void registerProxyRouter() { try { mMediaRouterService.registerProxyRouter( mClient, - context.getApplicationContext().getPackageName(), - clientPackageName, - user); + mContext.getApplicationContext().getPackageName(), + mClientPackageName, + mClientUser); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } - - mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; } @Override diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl index a7ec6c692416..8ce1b6d5264d 100644 --- a/media/java/android/media/projection/IMediaProjectionManager.aidl +++ b/media/java/android/media/projection/IMediaProjectionManager.aidl @@ -37,31 +37,41 @@ interface IMediaProjectionManager { const String EXTRA_PACKAGE_REUSING_GRANTED_CONSENT = "extra_media_projection_package_reusing_consent"; + /** + * Returns whether a combination of process UID and package has the projection permission. + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. + */ @UnsupportedAppUsage - boolean hasProjectionPermission(int uid, String packageName); + boolean hasProjectionPermission(int processUid, String packageName); /** * Returns a new {@link IMediaProjection} instance associated with the given package. + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. */ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IMediaProjection createProjection(int uid, String packageName, int type, + IMediaProjection createProjection(int processUid, String packageName, int type, boolean permanentGrant); /** * Returns the current {@link IMediaProjection} instance associated with the given - * package, or {@code null} if it is not possible to re-use the current projection. + * package and process UID, or {@code null} if it is not possible to re-use the current + * projection. * * <p>Should only be invoked when the user has reviewed consent for a re-used projection token. * Requires that there is a prior session waiting for the user to review consent, and the given * package details match those on the current projection. * * @see {@link #isCurrentProjection} + * + * @param processUid the process UID as returned by {@link android.os.Process.myUid()}. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IMediaProjection getProjection(int uid, String packageName); + IMediaProjection getProjection(int processUid, String packageName); /** * Returns {@code true} if the given {@link IMediaProjection} corresponds to the current @@ -162,8 +172,8 @@ interface IMediaProjectionManager { * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an + * app or SystemUI. * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED. * Indicates the entry point for requesting the permission. Must be * a valid state defined @@ -172,49 +182,49 @@ interface IMediaProjectionManager { @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource); + oneway void notifyPermissionRequestInitiated(int hostProcessUid, int sessionCreationSource); /** * Notifies system server that the permission request was displayed. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestDisplayed(int hostUid); + oneway void notifyPermissionRequestDisplayed(int hostProcessUid); /** * Notifies system server that the permission request was cancelled. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyPermissionRequestCancelled(int hostUid); + oneway void notifyPermissionRequestCancelled(int hostProcessUid); /** * Notifies system server that the app selector was displayed. * * <p>Only used for emitting atoms. * - * @param hostUid The uid of the process requesting consent to capture, may be an app or - * SystemUI. + * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or + * SystemUI. */ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - oneway void notifyAppSelectorDisplayed(int hostUid); + oneway void notifyAppSelectorDisplayed(int hostProcessUid); @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode); + void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode); } diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java index 262f5f198c22..096e8adf2f68 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java @@ -50,8 +50,16 @@ public final class BluetoothMidiDevice { private static final String TAG = "BluetoothMidiDevice"; private static final boolean DEBUG = false; - private static final int DEFAULT_PACKET_SIZE = 20; - private static final int MAX_PACKET_SIZE = 512; + // Bluetooth services should subtract 5 bytes from the MTU for headers. + private static final int HEADER_SIZE = 5; + // Min MTU size for BLE + private static final int MIN_L2CAP_MTU = 23; + // 23 (min L2CAP MTU) - 5 (header size) + private static final int DEFAULT_PACKET_SIZE = MIN_L2CAP_MTU - HEADER_SIZE; + // Max MTU size on Android + private static final int MAX_ANDROID_MTU = 517; + // 517 (max Android MTU) - 5 (header size) + private static final int MAX_PACKET_SIZE = MAX_ANDROID_MTU - HEADER_SIZE; // Bluetooth MIDI Gatt service UUID private static final UUID MIDI_SERVICE = UUID.fromString( @@ -135,8 +143,8 @@ public final class BluetoothMidiDevice { // switch to receiving notifications mBluetoothGatt.readCharacteristic(characteristic); - // Request higher MTU size - if (!gatt.requestMtu(MAX_PACKET_SIZE)) { + // Request max MTU size + if (!gatt.requestMtu(MAX_ANDROID_MTU)) { Log.e(TAG, "request mtu failed"); mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); @@ -204,8 +212,15 @@ public final class BluetoothMidiDevice { public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { Log.d(TAG, "onMtuChanged callback received. mtu: " + mtu + ", status: " + status); if (status == BluetoothGatt.GATT_SUCCESS) { - mPacketEncoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE)); - mPacketDecoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE)); + int packetSize = Math.min(mtu - HEADER_SIZE, MAX_PACKET_SIZE); + if (packetSize <= 0) { + Log.e(TAG, "onMtuChanged non-positive packet size: " + packetSize); + packetSize = DEFAULT_PACKET_SIZE; + } else if (packetSize < DEFAULT_PACKET_SIZE) { + Log.w(TAG, "onMtuChanged small packet size: " + packetSize); + } + mPacketEncoder.setMaxPacketSize(packetSize); + mPacketDecoder.setMaxPacketSize(packetSize); } else { mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); diff --git a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml deleted file mode 100644 index 9d16f32db932..000000000000 --- a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?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. - --> - -<!-- Copied from //frameworks/base/core/res/res/drawable/item_background_material.xml --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/autofill_light_colorControlHighlight"> - <item android:id="@android:id/mask"> - <color android:color="@android:color/white"/> - </item> -</ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml index 2f0c83b6556a..5becc86927d2 100644 --- a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml +++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2023 The Android Open Source Project + ~ Copyright (C) 2024 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. @@ -23,8 +23,8 @@ android:shape="rectangle" android:top="1dp"> <shape> - <corners android:radius="28dp" /> - <solid android:color="@android:color/system_surface_container_high_light" /> + <corners android:radius="16dp" /> + <solid android:color="@color/dropdown_container" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml deleted file mode 100644 index e4e9f7ac85a9..000000000000 --- a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml +++ /dev/null @@ -1,37 +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. - --> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@android:id/content" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/autofill.Dataset"> - <ImageView - android:id="@android:id/icon1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_alignParentStart="true" - android:background="@null"/> - <TextView - android:id="@android:id/text1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_toEndOf="@android:id/icon1" - style="@style/autofill.TextAppearance"/> - -</RelativeLayout> diff --git a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml new file mode 100644 index 000000000000..cb6c6b473244 --- /dev/null +++ b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml @@ -0,0 +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. + --> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxWidth="@dimen/autofill_dropdown_layout_width" + android:elevation="3dp"> + + <ImageView + android:id="@android:id/icon1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_alignParentStart="true" + android:background="@null"/> + <TextView + android:id="@android:id/text1" + android:layout_width="@dimen/autofill_dropdown_text_width" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_toEndOf="@android:id/icon1" + style="@style/autofill.TextTitle"/> + <TextView + android:id="@android:id/text2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@android:id/text1" + android:layout_toEndOf="@android:id/icon1" + style="@style/autofill.TextSubtitle"/> + +</RelativeLayout> diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml index 63b9f24d9033..dcb7ef9c3ed8 100644 --- a/packages/CredentialManager/res/values/colors.xml +++ b/packages/CredentialManager/res/values/colors.xml @@ -16,23 +16,8 @@ <!-- Color palette --> <resources> - <color name="autofill_light_colorPrimary">@color/primary_material_light</color> - <color name="autofill_light_colorAccent">@color/accent_material_light</color> - <color name="autofill_light_colorControlHighlight">@color/ripple_material_light</color> - <color name="autofill_light_colorButtonNormal">@color/button_material_light</color> - - <!-- Text colors --> - <color name="autofill_light_textColorPrimary">@color/abc_primary_text_material_light</color> - <color name="autofill_light_textColorSecondary">@color/abc_secondary_text_material_light</color> - <color name="autofill_light_textColorHint">@color/abc_hint_foreground_material_light</color> - <color name="autofill_light_textColorHintInverse">@color/abc_hint_foreground_material_dark - </color> - <color name="autofill_light_textColorHighlight">@color/highlighted_text_material_light</color> - <color name="autofill_light_textColorLink">@color/autofill_light_colorAccent</color> - <!-- These colors are used for Remote Views. --> - <color name="background_dark_mode">#0E0C0B</color> - <color name="background">#F1F3F4</color> - <color name="text_primary_dark_mode">#DFDEDB</color> - <color name="text_primary">#202124</color> + <color name="text_primary">#1A1B20</color> + <color name="text_secondary">#44474F</color> + <color name="dropdown_container">#F3F3FA</color> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml index 67003a330974..2a4719d027e2 100644 --- a/packages/CredentialManager/res/values/dimens.xml +++ b/packages/CredentialManager/res/values/dimens.xml @@ -17,6 +17,12 @@ --> <resources> - <dimen name="autofill_view_padding">16dp</dimen> - <dimen name="autofill_icon_size">16dp</dimen> + <dimen name="autofill_view_top_padding">12dp</dimen> + <dimen name="autofill_view_right_padding">24dp</dimen> + <dimen name="autofill_view_bottom_padding">12dp</dimen> + <dimen name="autofill_view_left_padding">16dp</dimen> + <dimen name="autofill_view_icon_to_text_padding">10dp</dimen> + <dimen name="autofill_icon_size">24dp</dimen> + <dimen name="autofill_dropdown_layout_width">296dp</dimen> + <dimen name="autofill_dropdown_text_width">240dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/styles.xml b/packages/CredentialManager/res/values/styles.xml index 4a5761acdd83..7de941ebd4a5 100644 --- a/packages/CredentialManager/res/values/styles.xml +++ b/packages/CredentialManager/res/values/styles.xml @@ -15,24 +15,13 @@ --> <resources> - <style name="autofill.TextAppearance.Small" parent="@style/autofill.TextAppearance"> - <item name="android:textSize">12sp</item> - </style> - - - <style name="autofill.Dataset" parent=""> - <item name="android:background">@drawable/autofill_light_selectable_item_background</item> - </style> - - <style name="autofill.TextAppearance" parent=""> - <item name="android:textColor">@color/autofill_light_textColorPrimary</item> - <item name="android:textColorHint">@color/autofill_light_textColorHint</item> - <item name="android:textColorHighlight">@color/autofill_light_textColorHighlight</item> - <item name="android:textColorLink">@color/autofill_light_textColorLink</item> + <style name="autofill.TextTitle" parent=""> + <item name="android:fontFamily">google-sans-medium</item> <item name="android:textSize">14sp</item> </style> - <style name="autofill.TextAppearance.Primary"> - <item name="android:textColor">@color/autofill_light_textColorPrimary</item> + <style name="autofill.TextSubtitle" parent=""> + <item name="android:fontFamily">google-sans-text</item> + <item name="android:textSize">12sp</item> </style> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index b2c23a401117..58467afe43a4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -57,6 +57,7 @@ import com.android.credentialmanager.common.ui.RemoteViewsFactory import com.android.credentialmanager.getflow.ProviderDisplayInfo import com.android.credentialmanager.getflow.toProviderDisplayInfo import com.android.credentialmanager.ktx.credentialEntry +import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.CredentialEntryInfo import com.android.credentialmanager.model.get.ProviderInfo import org.json.JSONException @@ -313,12 +314,14 @@ class CredentialAutofillService : AutofillService() { var i = 0 var datasetAdded = false - val duplicateDisplayNames: MutableMap<String, Boolean> = mutableMapOf() + val duplicateDisplayNamesForPasskeys: MutableMap<String, Boolean> = mutableMapOf() providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach { val credentialEntry = it.sortedCredentialEntryList.first() - credentialEntry.displayName?.let {displayName -> - val duplicateEntry = duplicateDisplayNames.contains(displayName) - duplicateDisplayNames[displayName] = duplicateEntry + if (credentialEntry.credentialType == CredentialType.PASSKEY) { + credentialEntry.displayName?.let { displayName -> + val duplicateEntry = duplicateDisplayNamesForPasskeys.contains(displayName) + duplicateDisplayNamesForPasskeys[displayName] = duplicateEntry + } } } providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{ @@ -355,12 +358,19 @@ class CredentialAutofillService : AutofillService() { } else { spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1] } - val displayName : String = primaryEntry.displayName ?: primaryEntry.userName + val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY + && primaryEntry.displayName != null) { + primaryEntry.displayName!! + } else { + primaryEntry.userName + } val sliceBuilder = InlineSuggestionUi .newContentBuilder(pendingIntent) .setTitle(displayName) sliceBuilder.setStartIcon(icon) - if (duplicateDisplayNames[displayName] == true) { + if (primaryEntry.credentialType == + CredentialType.PASSKEY && duplicateDisplayNamesForPasskeys[displayName] + == true) { sliceBuilder.setSubtitle(primaryEntry.userName) } inlinePresentation = InlinePresentation( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt index 4dc7f00c1550..e039dead043e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt @@ -21,6 +21,7 @@ import android.content.res.Configuration import android.widget.RemoteViews import androidx.core.content.ContextCompat import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.model.CredentialType import android.graphics.drawable.Icon class RemoteViewsFactory { @@ -29,48 +30,87 @@ class RemoteViewsFactory { private const val setAdjustViewBoundsMethodName = "setAdjustViewBounds" private const val setMaxHeightMethodName = "setMaxHeight" private const val setBackgroundResourceMethodName = "setBackgroundResource" + private const val bulletPoint = "\u2022" + private const val passwordCharacterLength = 15 fun createDropdownPresentation( context: Context, icon: Icon, credentialEntryInfo: CredentialEntryInfo ): RemoteViews { - val padding = context.resources.getDimensionPixelSize(com.android - .credentialmanager.R.dimen.autofill_view_padding) var layoutId: Int = com.android.credentialmanager.R.layout - .autofill_dataset_left_with_item_tag_hint + .credman_dropdown_presentation_layout val remoteViews = RemoteViews(context.packageName, layoutId) - setRemoteViewsPaddings(remoteViews, padding) - val textColorPrimary = getTextColorPrimary(isDarkMode(context), context); - remoteViews.setTextColor(android.R.id.text1, textColorPrimary); - remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) - + if (credentialEntryInfo.credentialType == CredentialType.UNKNOWN) { + return remoteViews + } + setRemoteViewsPaddings(remoteViews, context) + if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) { + val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName + remoteViews.setTextViewText(android.R.id.text1, displayName) + val secondaryText = if (credentialEntryInfo.displayName != null) + (credentialEntryInfo.userName + " " + bulletPoint + " " + + credentialEntryInfo.credentialTypeDisplayName + + " " + bulletPoint + " " + credentialEntryInfo.providerDisplayName) + else (credentialEntryInfo.credentialTypeDisplayName + " " + bulletPoint + " " + + credentialEntryInfo.providerDisplayName) + remoteViews.setTextViewText(android.R.id.text2, secondaryText) + } else { + remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) + remoteViews.setTextViewText(android.R.id.text2, + bulletPoint.repeat(passwordCharacterLength)) + } + val textColorPrimary = ContextCompat.getColor(context, + com.android.credentialmanager.R.color.text_primary) + remoteViews.setTextColor(android.R.id.text1, textColorPrimary) + val textColorSecondary = ContextCompat.getColor(context, com.android + .credentialmanager.R.color.text_secondary) + remoteViews.setTextColor(android.R.id.text2, textColorSecondary) remoteViews.setImageViewIcon(android.R.id.icon1, icon); remoteViews.setBoolean( android.R.id.icon1, setAdjustViewBoundsMethodName, true); remoteViews.setInt( android.R.id.icon1, - setMaxHeightMethodName, + setMaxHeightMethodName, context.resources.getDimensionPixelSize( com.android.credentialmanager.R.dimen.autofill_icon_size)); - val drawableId = if (isDarkMode(context)) - com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one_dark - else com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one + val drawableId = + com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one remoteViews.setInt( android.R.id.content, setBackgroundResourceMethodName, drawableId); return remoteViews } private fun setRemoteViewsPaddings( - remoteViews: RemoteViews, - padding: Int) { - val halfPadding = padding / 2 + remoteViews: RemoteViews, context: Context) { + val leftPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_left_padding) + val iconToTextPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding) + val rightPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_right_padding) + val topPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_top_padding) + val bottomPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_bottom_padding) + remoteViews.setViewPadding( + android.R.id.icon1, + leftPadding, + /* top=*/0, + /* right=*/0, + /* bottom=*/0) remoteViews.setViewPadding( android.R.id.text1, - halfPadding, - halfPadding, - halfPadding, - halfPadding) + iconToTextPadding, + /* top=*/topPadding, + /* right=*/rightPadding, + /* bottom=*/0) + remoteViews.setViewPadding( + android.R.id.text2, + iconToTextPadding, + /* top=*/0, + /* right=*/rightPadding, + /* bottom=*/bottomPadding) } private fun isDarkMode(context: Context): Boolean { @@ -78,11 +118,5 @@ class RemoteViewsFactory { Configuration.UI_MODE_NIGHT_MASK return currentNightMode == Configuration.UI_MODE_NIGHT_YES } - - private fun getTextColorPrimary(darkMode: Boolean, context: Context): Int { - return if (darkMode) ContextCompat.getColor( - context, com.android.credentialmanager.R.color.text_primary_dark_mode) - else ContextCompat.getColor(context, com.android.credentialmanager.R.color.text_primary) - } } } diff --git a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml index 7f1651721d84..8127e1a6d5a2 100644 --- a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml +++ b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" @@ -8,7 +8,7 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java" - line="78" + line="79" column="34"/> </issue> @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java" - line="90" + line="91" column="36"/> </issue> @@ -30,7 +30,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="43" + line="45" column="46"/> </issue> @@ -41,7 +41,7 @@ errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="65" + line="67" column="9"/> </issue> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="72" + line="74" column="9"/> </issue> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="80" + line="82" column="9"/> </issue> @@ -74,8 +74,8 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="105" + line="107" column="26"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index ffe3d1d633d7..5da4b9518a31 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -61,7 +61,7 @@ android_library { "src/**/*.kt", ], lint: { - baseline_filename: "lint-baseline.xml", + extra_check_modules: ["SettingsLibLintChecker"], }, } diff --git a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml index e9c687fd3227..13bf5f54ead3 100644 --- a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml +++ b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" @@ -8,7 +8,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="77" + line="81" column="41"/> </issue> @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="78" + line="82" column="45"/> </issue> @@ -30,18 +30,18 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="78" + line="82" column="62"/> </issue> <issue id="NewApi" message="Call requires API level 29 (current min is 21): `android.telephony.TelephonyManager#getEmergencyNumberList`" - errorLine1=" Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(" + errorLine1=" Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(" errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="173" + line="177" column="74"/> </issue> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="196" + line="200" column="41"/> </issue> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="219" + line="223" column="69"/> </issue> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="234" + line="238" column="41"/> </issue> @@ -85,8 +85,8 @@ errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="251" + line="255" column="52"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index d9f74dadf281..010a6ce9d4d9 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -28,7 +28,4 @@ android_library { "com.android.extservices", "com.android.healthfitness", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml b/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml deleted file mode 100644 index cfa64a487407..000000000000 --- a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> - - <issue - id="NewApi" - message="`@android:dimen/config_restrictedIconSize` requires API level 29 (current min is 28)" - errorLine1=' <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen>' - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml" - line="21" - column="52"/> - </issue> - -</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml index 26d05a621c22..45a07fe9eee3 100644 --- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml +++ b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml @@ -1,26 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" - errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" - errorLine2=" ~~"> + message="Call requires API level 28 (current min is 23): `android.content.Context#createPackageContextAsUser`" + errorLine1=" userContext = context.createPackageContextAsUser(context.getPackageName(), 0, user);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="97" - column="56"/> + line="64" + column="35"/> </issue> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" - errorLine1=" return um.getUserProfiles().contains(UserHandle.of(userId));" - errorLine2=" ~~"> + message="Call requires API level 29 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerUser`" + errorLine1=" if (Objects.equals(dpm.getDeviceOwnerUser(), user)) {" + errorLine2=" ~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="140" - column="57"/> + line="74" + column="32"/> </issue> <issue @@ -36,35 +36,35 @@ <issue id="NewApi" - message="Call requires API level 28 (current min is 23): `android.content.Context#createPackageContextAsUser`" - errorLine1=" userContext = context.createPackageContextAsUser(context.getPackageName(), 0, user);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" + errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="64" - column="35"/> + line="97" + column="56"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerUser`" - errorLine1=" if (Objects.equals(dpm.getDeviceOwnerUser(), user)) {" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 23): `android.content.Context#startActivityAsUser`" + errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="74" - column="32"/> + line="97" + column="17"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 23): `android.content.Context#startActivityAsUser`" - errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" + errorLine1=" return um.getUserProfiles().contains(UserHandle.of(userId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="97" - column="17"/> + line="120" + column="57"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml index 0744710e5224..db6a88210cd4 100644 --- a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml +++ b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" diff --git a/packages/SettingsLib/SearchProvider/lint-baseline.xml b/packages/SettingsLib/SearchProvider/lint-baseline.xml index 53346e030be2..3cfca1d2cdcb 100644 --- a/packages/SettingsLib/SearchProvider/lint-baseline.xml +++ b/packages/SettingsLib/SearchProvider/lint-baseline.xml @@ -1,36 +1,47 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`" - errorLine1=" super(" - errorLine2=" ~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexablesProvider`" + errorLine1="public abstract class SettingsXmlIndexProvider extends SearchIndexablesProvider {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="107" - column="13"/> + line="34" + column="56"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`" - errorLine1=" public static final class SearchIndexableIntentResource extends SearchIndexableResource {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`" + errorLine1=" final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="97" - column="69"/> + line="45" + column="54"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexablesProvider`" - errorLine1="public abstract class SettingsXmlIndexProvider extends SearchIndexablesProvider {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`" + errorLine1=" .add(XmlResource.COLUMN_RANK, indexableResource.rank)" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="34" + line="50" + column="51"/> + </issue> + + <issue + id="NewApi" + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`" + errorLine1=" .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" + line="51" column="56"/> </issue> @@ -58,79 +69,68 @@ <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" - errorLine1=' this.intentAction = "android.intent.action.MAIN";' - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="113" - column="17"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" - errorLine1=" this.intentAction = intentAction;" - errorLine2=" ~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" + errorLine1=" indexableResource.intentTargetClass);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="115" - column="17"/> + line="56" + column="29"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" - errorLine1=" indexableResource.intentTargetClass);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`" + errorLine1=" public static final class SearchIndexableIntentResource extends SearchIndexableResource {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="56" - column="29"/> + line="97" + column="69"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" - errorLine1=" this.intentTargetClass = className;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`" + errorLine1=" super(" + errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="117" + line="107" column="13"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`" - errorLine1=" .add(XmlResource.COLUMN_RANK, indexableResource.rank)" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" + errorLine1=' this.intentAction = "android.intent.action.MAIN";' + errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="50" - column="51"/> + line="113" + column="17"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`" - errorLine1=" .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" + errorLine1=" this.intentAction = intentAction;" + errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="51" - column="56"/> + line="115" + column="17"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`" - errorLine1=" final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" + errorLine1=" this.intentTargetClass = className;" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="45" - column="54"/> + line="117" + column="13"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt index a9974dc7d389..514ad66919ee 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt @@ -39,6 +39,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.framework.theme.SettingsTheme @@ -68,6 +71,7 @@ fun Spinner(options: List<SpinnerOption>, selectedId: Int?, setId: (id: Int) -> ) { val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd) Button( + modifier = Modifier.semantics { role = Role.DropdownList }, onClick = { expanded = true }, colors = ButtonDefaults.buttonColors( containerColor = SettingsTheme.colorScheme.spinnerHeaderContainer, diff --git a/packages/SettingsLib/Tile/lint-baseline.xml b/packages/SettingsLib/Tile/lint-baseline.xml index 326ec0dbaa72..56b1bcafbae2 100644 --- a/packages/SettingsLib/Tile/lint-baseline.xml +++ b/packages/SettingsLib/Tile/lint-baseline.xml @@ -1,55 +1,59 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `java.lang.Iterable#forEach`" - errorLine1=" controllers.forEach(controller -> {" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java" - line="79" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`"> + message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`" + errorLine1=" dest.writeBoolean(this instanceof ProviderTile);" + errorLine2=" ~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="312"/> + line="114" + column="14"/> </issue> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`"> + message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`" + errorLine1=" final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);" + errorLine2=" ~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="318"/> + line="326" + column="36"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`"> + message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`" + errorLine1=" icon.setTint(tintColor);" + errorLine2=" ~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="373"/> + line="332" + column="22"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`"> + message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`" + errorLine1=" final boolean isProviderTile = source.readBoolean();" + errorLine2=" ~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="108"/> + line="387" + column="51"/> </issue> <issue id="NewApi" - message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`"> + message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`" + errorLine1=" return provider.call(context.getAttributionSource()," + errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java" - line="565"/> + line="601" + column="42"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Utils/lint-baseline.xml b/packages/SettingsLib/Utils/lint-baseline.xml index 3fcd56c557e8..2f6cc3ae8719 100644 --- a/packages/SettingsLib/Utils/lint-baseline.xml +++ b/packages/SettingsLib/Utils/lint-baseline.xml @@ -1,26 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" - errorLine1=" mContext.getPackageName(), 0, UserHandle.of(managedUserId)" - errorLine2=" ~~"> - <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="119" - column="70"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" - errorLine1=" intent, 0, UserHandle.of(managedUserId));" - errorLine2=" ~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`" + errorLine1=" return context.getSystemService(UserManager.class).isManagedProfile(userId)" + errorLine2=" ~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="150" - column="47"/> + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java" + line="62" + column="60"/> </issue> <issue @@ -36,68 +25,68 @@ <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`" - errorLine1=" if (mUserManager.isManagedProfile(id)) {" - errorLine2=" ~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`" + errorLine1=" activityContext.startActivityAsUser(intent, UserHandle.of(userId));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="173" - column="30"/> + line="80" + column="29"/> </issue> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`" - errorLine1=" return context.getSystemService(UserManager.class).isManagedProfile(userId)" - errorLine2=" ~~~~~~~~~~~~~~~~"> + message="Call requires API level 28 (current min is 21): `android.content.Context#createPackageContextAsUser`" + errorLine1=" mContext.createPackageContextAsUser(" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java" - line="62" - column="60"/> + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" + line="118" + column="30"/> </issue> <issue id="NewApi" - message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`" - errorLine1=" return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" + errorLine1=" mContext.getPackageName(), 0, UserHandle.of(managedUserId)" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="163" - column="37"/> + line="119" + column="70"/> </issue> <issue id="NewApi" - message="Call requires API level 28 (current min is 21): `android.content.Context#createPackageContextAsUser`" - errorLine1=" mContext.createPackageContextAsUser(" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 21): `android.content.pm.PackageManager#queryIntentActivitiesAsUser`" + errorLine1=" mPackageManager.queryIntentActivitiesAsUser(" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="118" - column="30"/> + line="149" + column="33"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`" - errorLine1=" activityContext.startActivityAsUser(intent, UserHandle.of(userId));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" + errorLine1=" intent, 0, UserHandle.of(managedUserId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="80" - column="29"/> + line="150" + column="47"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.content.pm.PackageManager#queryIntentActivitiesAsUser`" - errorLine1=" mPackageManager.queryIntentActivitiesAsUser(" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`" + errorLine1=" return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="149" - column="33"/> + line="163" + column="37"/> </issue> <issue @@ -111,4 +100,15 @@ column="53"/> </issue> + <issue + id="NewApi" + message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`" + errorLine1=" if (mUserManager.isManagedProfile(id)) {" + errorLine2=" ~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" + line="173" + column="30"/> + </issue> + </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/lint-baseline.xml b/packages/SettingsLib/lint-baseline.xml deleted file mode 100644 index d6a23fd827d9..000000000000 --- a/packages/SettingsLib/lint-baseline.xml +++ /dev/null @@ -1,204 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.bluetooth.BluetoothDevice#setAlias`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java" - line="584"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java" - line="248"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java" - line="278"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#registerSubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="201"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#unregisterSubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="208"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.os.UserManager#isUserForeground`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java" - line="78"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/Utils.java" - line="498"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java" - line="225"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="215"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="86"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="222"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="88"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java" - line="66"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java" - line="49"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java" - line="33"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.net.wifi.WifiManager.SubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="64"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="125"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.CarrierNetworkListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="124"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataActivityListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="123"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataConnectionStateListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="122"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DisplayInfoListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="126"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ServiceStateListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="120"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.SignalStrengthsListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="121"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="79"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="119"/> - </issue> - -</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp index 3b14712cc87e..390c9d2e98de 100644 --- a/packages/SettingsLib/search/Android.bp +++ b/packages/SettingsLib/search/Android.bp @@ -12,9 +12,6 @@ java_library { visibility: ["//visibility:private"], srcs: ["interface-src/**/*.java"], host_supported: true, - lint: { - baseline_filename: "lint-baseline.xml", - }, } android_library { diff --git a/packages/SettingsLib/search/lint-baseline.xml b/packages/SettingsLib/search/lint-baseline.xml index 7ec512b617d7..61cdb051feeb 100644 --- a/packages/SettingsLib/search/lint-baseline.xml +++ b/packages/SettingsLib/search/lint-baseline.xml @@ -1,26 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`" - errorLine1=" super(context);" - errorLine2=" ~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableData`" + errorLine1="public class SearchIndexableRaw extends SearchIndexableData {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java" - line="62" - column="9"/> + line="29" + column="41"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableData`" - errorLine1="public class SearchIndexableRaw extends SearchIndexableData {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`" + errorLine1=" super(context);" + errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java" - line="29" - column="41"/> + line="62" + column="9"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index 8e5396fef744..d9024575f247 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -19,6 +19,7 @@ package com.android.settingslib; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; import static android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY; import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; +import static android.app.role.RoleManager.ROLE_FINANCED_DEVICE_KIOSK; import static com.android.settingslib.Utils.getColorAttrDefaultColor; @@ -27,6 +28,7 @@ import android.annotation.UserIdInt; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -70,6 +72,10 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); private static final Set<String> ECM_KEYS = new ArraySet<>(); + // TODO(b/281701062): reference role name from role manager once its exposed. + private static final String ROLE_DEVICE_LOCK_CONTROLLER = + "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER"; + static { if (android.security.Flags.extendEcmToAllSettings()) { ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); @@ -476,16 +482,27 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** - * Check if {@param packageName} is restricted by the profile or device owner from using - * metered data. + * Check if user control over metered data usage of {@code packageName} is disabled by the + * profile or device owner. * * @return EnforcedAdmin object containing the enforced admin component and admin user details, - * or {@code null} if the {@param packageName} is not restricted. + * or {@code null} if the user control is not disabled. */ - public static EnforcedAdmin checkIfMeteredDataRestricted(Context context, + public static EnforcedAdmin checkIfMeteredDataUsageUserControlDisabled(Context context, String packageName, int userId) { + RoleManager roleManager = context.getSystemService(RoleManager.class); + UserHandle userHandle = getUserHandleOf(userId); + if (roleManager.getRoleHoldersAsUser(ROLE_FINANCED_DEVICE_KIOSK, userHandle) + .contains(packageName) + || roleManager.getRoleHoldersAsUser(ROLE_DEVICE_LOCK_CONTROLLER, userHandle) + .contains(packageName)) { + // There is no actual device admin for a financed device, but metered data usage + // control should still be disabled for both controller and kiosk apps. + return new EnforcedAdmin(); + } + final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, - getUserHandleOf(userId)); + userHandle); if (enforcedAdmin == null) { return null; } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 1481d97a04fa..d26c5fffd932 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -867,6 +867,7 @@ public class SettingsBackupTest { Settings.Secure.NEARBY_SHARING_SLICE_URI, Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, + Settings.Secure.PRIVATE_SPACE_AUTO_LOCK, Settings.Secure.RELEASE_COMPRESS_BLOCKS_ON_INSTALL, Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, Settings.Secure.SHOW_QR_CODE_SCANNER_SETTING, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index d3a89f447d1f..d61ae7eccc42 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -438,6 +438,7 @@ android_robolectric_test { "androidx.core_core-animation-testing", "androidx.test.ext.junit", "inline-mockito-robolectric-prebuilt", + "platform-parametric-runner-lib", ], libs: [ "android.test.runner", diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig index 21263a92ae26..f7b1a26c9df9 100644 --- a/packages/SystemUI/aconfig/accessibility.aconfig +++ b/packages/SystemUI/aconfig/accessibility.aconfig @@ -10,6 +10,13 @@ flag { } flag { + name: "floating_menu_drag_to_hide" + namespace: "accessibility" + description: "Allows users to hide the FAB then use notification to dismiss or bring it back." + bug: "298718415" +} + +flag { name: "floating_menu_ime_displacement_animation" namespace: "accessibility" description: "Adds an animation for when the FAB is displaced by an IME becoming visible." @@ -28,4 +35,4 @@ flag { namespace: "accessibility" description: "Animates the floating menu's transition between curved and jagged edges." bug: "281140482" -}
\ No newline at end of file +} diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 5b218549de43..5fbffbed7965 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -258,6 +258,15 @@ flag { } flag { + name: "centralized_status_bar_dimens_refactor" + namespace: "systemui" + description: "Refactors shade header and keyguard status bar to read status bar dimens from a" + " central place, instead of reading resources directly. This is to take into account display" + " cutouts and other special cases. " + bug: "317199366" +} + +flag { name: "enable_layout_tracing" namespace: "systemui" description: "Enables detailed traversal slices during measure and layout in perfetto traces" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index 249b3e14ec72..2fea6a56daaf 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close @@ -21,10 +20,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey @@ -33,15 +30,14 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout -import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions +import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.transform @@ -66,7 +62,6 @@ val sceneTransitions = transitions { * This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture * handling and transitions before the full Flexiglass layout is ready. */ -@OptIn(ExperimentalComposeUiApi::class, ExperimentalCoroutinesApi::class) @Composable fun CommunalContainer( modifier: Modifier = Modifier, @@ -76,9 +71,15 @@ fun CommunalContainer( viewModel.currentScene .transform { value -> emit(value.toTransitionSceneKey()) } .collectAsState(TransitionSceneKey.Blank) - val sceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) } - // Don't show hub mode UI if keyguard is present. This is important since we're in the shade, - // which can be opened from many locations. + val sceneTransitionLayoutState = + updateSceneTransitionLayoutState( + currentScene, + onChangeScene = { viewModel.onSceneChanged(it.toCommunalSceneKey()) }, + transitions = sceneTransitions, + ) + + // Don't show hub mode UI if keyguard is not present. This is important since we're in the + // shade, which can be opened from many locations. val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false) // Failsafe to hide the whole SceneTransitionLayout in case of bugginess. @@ -96,59 +97,30 @@ fun CommunalContainer( onDispose { viewModel.setTransitionState(null) } } - Box(modifier = modifier.fillMaxSize()) { - SceneTransitionLayout( - modifier = Modifier.fillMaxSize(), - currentScene = currentScene, - onChangeScene = { sceneKey -> viewModel.onSceneChanged(sceneKey.toCommunalSceneKey()) }, - transitions = sceneTransitions, - state = sceneTransitionLayoutState, - edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize) + SceneTransitionLayout( + state = sceneTransitionLayoutState, + modifier = modifier.fillMaxSize(), + edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize), + ) { + scene( + TransitionSceneKey.Blank, + userActions = + mapOf( + Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to TransitionSceneKey.Communal + ) ) { - scene( - TransitionSceneKey.Blank, - userActions = - mapOf( - Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to - TransitionSceneKey.Communal - ) - ) { - BlankScene { showSceneTransitionLayout = false } - } - - scene( - TransitionSceneKey.Communal, - userActions = - mapOf( - Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to - TransitionSceneKey.Blank - ), - ) { - CommunalScene(viewModel, modifier = modifier) - } + BlankScene { showSceneTransitionLayout = false } } - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't - // block touches anymore. - Box( - modifier = - Modifier.fillMaxSize() - // Offsetting to the left so that edge swipe to open the hub still works. This - // does mean that the very right edge of the hub won't refresh the screen - // timeout, but should be good enough for a temporary solution. - .offset(x = -ContainerDimensions.EdgeSwipeSize) - .pointerInteropFilter { - viewModel.onUserActivity() - if ( - sceneTransitionLayoutState.transitionState.currentScene == - TransitionSceneKey.Blank - ) { - viewModel.onOuterTouch(it) - return@pointerInteropFilter true - } - false - } - ) + scene( + TransitionSceneKey.Communal, + userActions = + mapOf( + Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to TransitionSceneKey.Blank + ), + ) { + CommunalScene(viewModel, modifier = modifier) + } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 4eb9089dc589..c35202cd830a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -38,11 +37,11 @@ import com.android.compose.animation.scene.Edge as SceneTransitionEdge import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey import com.android.compose.animation.scene.SceneTransitionLayout -import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction import com.android.compose.animation.scene.observableTransitionState +import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.Edge @@ -82,7 +81,12 @@ fun SceneContainer( val currentScene = checkNotNull(sceneByKey[currentSceneKey]) val currentDestinations: Map<UserAction, SceneModel> by currentScene.destinationScenes.collectAsState() - val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) } + val state = + updateSceneTransitionLayoutState( + currentSceneKey.toTransitionSceneKey(), + onChangeScene = viewModel::onSceneChanged, + transitions = SceneContainerTransitions, + ) DisposableEffect(viewModel, state) { viewModel.setTransitionState(state.observableTransitionState().map { it.toModel() }) @@ -93,9 +97,6 @@ fun SceneContainer( modifier = Modifier.fillMaxSize(), ) { SceneTransitionLayout( - currentScene = currentSceneKey.toTransitionSceneKey(), - onChangeScene = viewModel::onSceneChanged, - transitions = SceneContainerTransitions, state = state, modifier = modifier diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt index ba6d00e3b7f5..7d3b0fbe1725 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -28,9 +28,9 @@ import kotlinx.coroutines.launch * the currently running transition, if there is one. */ internal fun CoroutineScope.animateToScene( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, target: SceneKey, -) { +): TransitionState.Transition? { val transitionState = layoutState.transitionState if (transitionState.currentScene == target) { // This can happen in 3 different situations, for which there isn't anything else to do: @@ -41,10 +41,10 @@ internal fun CoroutineScope.animateToScene( // a. didn't release their pointer yet. // b. released their pointer such that the swipe gesture was cancelled and the // transition is currently animating back to [target]. - return + return null } - when (transitionState) { + return when (transitionState) { is TransitionState.Idle -> animate(layoutState, target) is TransitionState.Transition -> { // A transition is currently running: first check whether `transition.toScene` or @@ -62,47 +62,43 @@ internal fun CoroutineScope.animateToScene( // finish the current transition early to make sure that the current state // change is committed. layoutState.finishTransition(transitionState, transitionState.currentScene) + null } else { // The transition is in progress: start the canned animation at the same // progress as it was in. // TODO(b/290184746): Also take the current velocity into account. animate(layoutState, target, startProgress = progress) } - - return - } - - if (transitionState.fromScene == target) { + } else if (transitionState.fromScene == target) { // There is a transition from [target] to another scene: simply animate the same // transition progress to `0`. - check(transitionState.toScene == transitionState.currentScene) + val progress = transitionState.progress if (progress.absoluteValue < ProgressVisibilityThreshold) { // The transition is at progress ~= 0: no need to animate.We finish the current // transition early to make sure that the current state change is committed. layoutState.finishTransition(transitionState, transitionState.currentScene) + null } else { // TODO(b/290184746): Also take the current velocity into account. animate(layoutState, target, startProgress = progress, reversed = true) } - - return + } else { + // Generic interruption; the current transition is neither from or to [target]. + // TODO(b/290930950): Better handle interruptions here. + animate(layoutState, target) } - - // Generic interruption; the current transition is neither from or to [target]. - // TODO(b/290930950): Better handle interruptions here. - animate(layoutState, target) } } } private fun CoroutineScope.animate( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, target: SceneKey, startProgress: Float = 0f, reversed: Boolean = false, -) { +): TransitionState.Transition { val fromScene = layoutState.transitionState.currentScene val isUserInput = (layoutState.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput @@ -143,10 +139,15 @@ private fun CoroutineScope.animate( } // Animate the progress to its target value. - launch { - animatable.animateTo(targetProgress, animationSpec) - layoutState.finishTransition(transition, target) - } + launch { animatable.animateTo(targetProgress, animationSpec) } + .invokeOnCompletion { + // Settle the state to Idle(target). Note that this will do nothing if this transition + // was replaced/interrupted by another one, and this also runs if this coroutine is + // cancelled, i.e. if [this] coroutine scope is cancelled. + layoutState.finishTransition(transition, target) + } + + return transition } private class OneOffTransition( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 280fbfb7d3d3..a910bca078e8 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -20,10 +20,10 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.isSpecified import androidx.compose.ui.geometry.isUnspecified import androidx.compose.ui.geometry.lerp import androidx.compose.ui.graphics.drawscope.ContentDrawScope @@ -46,41 +46,18 @@ import kotlinx.coroutines.launch /** An element on screen, that can be composed in one or more scenes. */ @Stable internal class Element(val key: ElementKey) { - /** - * The last state of this element, coming from any scene. Note that this state will be unstable - * if this element is present in multiple scenes but the shared element animation is disabled, - * given that multiple instances of the element with different states will write to this state. - * You should prefer using [SceneState.lastState] in the current scene when it is defined. - */ - val lastSharedState = State() - /** The mapping between a scene and the state this element has in that scene, if any. */ - val sceneStates = mutableMapOf<SceneKey, SceneState>() + // TODO(b/316901148): Make this a normal map instead once we can make sure that new transitions + // are first seen by composition then layout/drawing code. See 316901148#comment2 for details. + val sceneStates = SnapshotStateMap<SceneKey, SceneState>() override fun toString(): String { return "Element(key=$key)" } - /** The state of this element, either in a specific scene or in a shared context. */ - class State { - /** The offset of the element, relative to the SceneTransitionLayout containing it. */ - var offset = Offset.Unspecified - - /** The size of this element. */ - var size = SizeUnspecified - - /** The draw scale of this element. */ - var drawScale = Scale.Default - - /** The alpha of this element. */ - var alpha = AlphaUnspecified - } - /** The last and target state of this element in a given scene. */ @Stable class SceneState(val scene: SceneKey) { - val lastState = State() - var targetSize by mutableStateOf(SizeUnspecified) var targetOffset by mutableStateOf(Offset.Unspecified) @@ -94,7 +71,6 @@ internal class Element(val key: ElementKey) { companion object { val SizeUnspecified = IntSize(Int.MAX_VALUE, Int.MAX_VALUE) - val AlphaUnspecified = Float.MIN_VALUE } } @@ -219,7 +195,7 @@ internal class ElementNode( } override fun ContentDrawScope.draw() { - val drawScale = getDrawScale(layoutImpl, element, scene, sceneState) + val drawScale = getDrawScale(layoutImpl, element, scene) if (drawScale == Scale.Default) { drawContent() } else { @@ -264,7 +240,6 @@ private fun shouldDrawElement( // Always draw the element if there is no ongoing transition or if the element is not shared. if ( transition == null || - !layoutImpl.isTransitionReady(transition) || transition.fromScene !in element.sceneStates || transition.toScene !in element.sceneStates ) { @@ -304,7 +279,7 @@ internal fun shouldDrawOrComposeSharedElement( } private fun isSharedElementEnabled( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, transition: TransitionState.Transition, element: ElementKey, ): Boolean { @@ -312,7 +287,7 @@ private fun isSharedElementEnabled( } internal fun sharedElementTransformation( - layoutState: SceneTransitionLayoutStateImpl, + layoutState: BaseSceneTransitionLayoutState, transition: TransitionState.Transition, element: ElementKey, ): SharedElementTransformation? { @@ -342,18 +317,9 @@ private fun isElementOpaque( layoutImpl: SceneTransitionLayoutImpl, element: Element, scene: Scene, - sceneState: Element.SceneState, ): Boolean { val transition = layoutImpl.state.currentTransition ?: return true - if (!layoutImpl.isTransitionReady(transition)) { - val lastValue = - sceneState.lastState.alpha.takeIf { it != Element.AlphaUnspecified } - ?: element.lastSharedState.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f - - return lastValue == 1f - } - val fromScene = transition.fromScene val toScene = transition.toScene val fromState = element.sceneStates[fromScene] @@ -383,7 +349,6 @@ private fun elementAlpha( layoutImpl: SceneTransitionLayoutImpl, element: Element, scene: Scene, - sceneState: Element.SceneState, ): Float { return computeValue( layoutImpl, @@ -393,10 +358,7 @@ private fun elementAlpha( transformation = { it.alpha }, idleValue = 1f, currentValue = { 1f }, - lastValue = { - sceneState.lastState.alpha.takeIf { it != Element.AlphaUnspecified } - ?: element.lastSharedState.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f - }, + isSpecified = { true }, ::lerp, ) .coerceIn(0f, 1f) @@ -434,34 +396,23 @@ private fun IntermediateMeasureScope.measure( transformation = { it.size }, idleValue = lookaheadSize, currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() }, - lastValue = { - sceneState.lastState.size.takeIf { it != Element.SizeUnspecified } - ?: element.lastSharedState.size.takeIf { it != Element.SizeUnspecified } - ?: measurable.measure(constraints).also { maybePlaceable = it }.size() - }, + isSpecified = { it != Element.SizeUnspecified }, ::lerp, ) - val placeable = - maybePlaceable - ?: measurable.measure( - Constraints.fixed( - targetSize.width.coerceAtLeast(0), - targetSize.height.coerceAtLeast(0), - ) + return maybePlaceable + ?: measurable.measure( + Constraints.fixed( + targetSize.width.coerceAtLeast(0), + targetSize.height.coerceAtLeast(0), ) - - val size = placeable.size() - element.lastSharedState.size = size - sceneState.lastState.size = size - return placeable + ) } private fun getDrawScale( layoutImpl: SceneTransitionLayoutImpl, element: Element, - scene: Scene, - sceneState: Element.SceneState + scene: Scene ): Scale { return computeValue( layoutImpl, @@ -471,10 +422,7 @@ private fun getDrawScale( transformation = { it.drawScale }, idleValue = Scale.Default, currentValue = { Scale.Default }, - lastValue = { - sceneState.lastState.drawScale.takeIf { it != Scale.Default } - ?: element.lastSharedState.drawScale - }, + isSpecified = { true }, ::lerp, ) } @@ -498,9 +446,12 @@ private fun IntermediateMeasureScope.place( sceneState.targetOffset = targetOffsetInScene } + // No need to place the element in this scene if we don't want to draw it anyways. + if (!shouldDrawElement(layoutImpl, scene, element)) { + return + } + val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero) - val lastSharedState = element.lastSharedState - val lastSceneState = sceneState.lastState val targetOffset = computeValue( layoutImpl, @@ -510,37 +461,19 @@ private fun IntermediateMeasureScope.place( transformation = { it.offset }, idleValue = targetOffsetInScene, currentValue = { currentOffset }, - lastValue = { - lastSceneState.offset.takeIf { it.isSpecified } - ?: lastSharedState.offset.takeIf { it.isSpecified } ?: currentOffset - }, + isSpecified = { it != Offset.Unspecified }, ::lerp, ) - lastSharedState.offset = targetOffset - lastSceneState.offset = targetOffset - - // No need to place the element in this scene if we don't want to draw it anyways. Note that - // it's still important to compute the target offset and update last(Shared|Scene)State, - // otherwise they will be out of date. - if (!shouldDrawElement(layoutImpl, scene, element)) { - return - } - val offset = (targetOffset - currentOffset).round() - if (isElementOpaque(layoutImpl, element, scene, sceneState)) { + if (isElementOpaque(layoutImpl, element, scene)) { // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not // animated once b/305195729 is fixed. Test that drawing is not invalidated in that // case. placeable.place(offset) - lastSharedState.alpha = 1f - lastSceneState.alpha = 1f } else { placeable.placeWithLayer(offset) { - val alpha = elementAlpha(layoutImpl, element, scene, sceneState) - this.alpha = alpha - lastSharedState.alpha = alpha - lastSceneState.alpha = alpha + this.alpha = elementAlpha(layoutImpl, element, scene) } } } @@ -563,8 +496,6 @@ private fun IntermediateMeasureScope.place( * different than [idleValue] even if the value is not transformed directly because it could be * impacted by the transformations on other elements, like a parent that is being translated or * resized. - * @param lastValue the last value that was used. This should be equal to [currentValue] if this is - * the first time the value is set. * @param lerp the linear interpolation function used to interpolate between two values of this * value type. */ @@ -576,7 +507,7 @@ private inline fun <T> computeValue( transformation: (ElementTransformations) -> PropertyTransformation<T>?, idleValue: T, currentValue: () -> T, - lastValue: () -> T, + isSpecified: (T) -> Boolean, lerp: (T, T, Float) -> T, ): T { val transition = @@ -587,21 +518,16 @@ private inline fun <T> computeValue( // layout phase. ?: return currentValue() - // A transition was started but it's not ready yet (not all elements have been composed/laid - // out yet). Use the last value that was set, to make sure elements don't unexpectedly jump. - if (!layoutImpl.isTransitionReady(transition)) { - return lastValue() - } - val fromScene = transition.fromScene val toScene = transition.toScene + val fromState = element.sceneStates[fromScene] val toState = element.sceneStates[toScene] if (fromState == null && toState == null) { // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not // run anymore. - return lastValue() + return idleValue } // The element is shared: interpolate between the value in fromScene and the value in toScene. @@ -612,6 +538,11 @@ private inline fun <T> computeValue( val start = sceneValue(fromState!!) val end = sceneValue(toState!!) + // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all + // nodes before the intermediate layout pass. + if (!isSpecified(start)) return end + if (!isSpecified(end)) return start + // Make sure we don't read progress if values are the same and we don't need to interpolate, // so we don't invalidate the phase where this is read. return if (start == end) start else lerp(start, end, transition.progress) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index af3c0999c97b..cdc4778dbf4d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -174,22 +174,6 @@ private fun shouldComposeMovableElement( // If we are idle, there is only one [scene] that is composed so we can compose our // movable content here. ?: return true - val fromScene = transition.fromScene - val toScene = transition.toScene - - val fromReady = layoutImpl.isSceneReady(fromScene) - val toReady = layoutImpl.isSceneReady(toScene) - - if (!fromReady && !toReady) { - // Neither of the scenes will be drawn, so where we compose it doesn't really matter. Note - // that we could have slightly more complicated logic here to optimize for this case, but - // it's not worth it given that readyScenes should disappear soon (b/316901148). - return scene == toScene - } - - // If one of the scenes is not ready, compose it in the other one to make sure it is drawn. - if (!fromReady) return scene == toScene - if (!toReady) return scene == fromScene // Always compose movable elements in the scene picked by their scene picker. return shouldDrawOrComposeSharedElement( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt index 454c0ecf8ac5..105f5728a836 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt @@ -16,96 +16,121 @@ package com.android.compose.animation.scene +import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size -import androidx.compose.ui.geometry.toRect import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.Outline -import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.graphics.drawscope.translate -import androidx.compose.ui.graphics.withSaveLayer +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.node.DelegatingNode import androidx.compose.ui.node.DrawModifierNode +import androidx.compose.ui.node.GlobalPositionAwareModifierNode +import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.toSize -internal fun Modifier.punchHole( - layoutImpl: SceneTransitionLayoutImpl, - element: ElementKey, - bounds: ElementKey, - shape: Shape, -): Modifier = this.then(PunchHoleElement(layoutImpl, element, bounds, shape)) +/** + * Punch a hole in this node with the given [size], [offset] and [shape]. + * + * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area. + * This can be used to make content drawn below an opaque element visible. For example, if we have + * [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below + * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big clock + * time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be the + * result. + */ +@Stable +fun Modifier.punchHole( + size: () -> Size, + offset: () -> Offset, + shape: Shape = RectangleShape, +): Modifier = this.then(PunchHoleElement(size, offset, shape)) + +/** + * Punch a hole in this node using the bounds of [coords] and the given [shape]. + * + * You can use [androidx.compose.ui.layout.onGloballyPositioned] to get the last coordinates of a + * node. + */ +@Stable +fun Modifier.punchHole( + coords: () -> LayoutCoordinates?, + shape: Shape = RectangleShape, +): Modifier = this.then(PunchHoleWithBoundsElement(coords, shape)) private data class PunchHoleElement( - private val layoutImpl: SceneTransitionLayoutImpl, - private val element: ElementKey, - private val bounds: ElementKey, + private val size: () -> Size, + private val offset: () -> Offset, private val shape: Shape, ) : ModifierNodeElement<PunchHoleNode>() { - override fun create(): PunchHoleNode = PunchHoleNode(layoutImpl, element, bounds, shape) + override fun create(): PunchHoleNode = PunchHoleNode(size, offset, { shape }) override fun update(node: PunchHoleNode) { - node.layoutImpl = layoutImpl - node.element = element - node.bounds = bounds - node.shape = shape + node.size = size + node.offset = offset + node.shape = { shape } } } private class PunchHoleNode( - var layoutImpl: SceneTransitionLayoutImpl, - var element: ElementKey, - var bounds: ElementKey, - var shape: Shape, -) : Modifier.Node(), DrawModifierNode { + var size: () -> Size, + var offset: () -> Offset, + var shape: () -> Shape, +) : Modifier.Node(), DrawModifierNode, LayoutModifierNode { private var lastSize: Size = Size.Unspecified private var lastLayoutDirection: LayoutDirection = LayoutDirection.Ltr private var lastOutline: Outline? = null - override fun ContentDrawScope.draw() { - val bounds = layoutImpl.elements[bounds] + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + return measurable.measure(constraints).run { + layout(width, height) { + placeWithLayer(0, 0) { compositingStrategy = CompositingStrategy.Offscreen } + } + } + } - if ( - bounds == null || - bounds.lastSharedState.size == Element.SizeUnspecified || - bounds.lastSharedState.offset == Offset.Unspecified - ) { + override fun ContentDrawScope.draw() { + val holeSize = size() + if (holeSize == Size.Zero) { drawContent() return } - val element = layoutImpl.elements.getValue(element) - drawIntoCanvas { canvas -> - canvas.withSaveLayer(size.toRect(), Paint()) { - drawContent() + drawContent() - val offset = bounds.lastSharedState.offset - element.lastSharedState.offset - translate(offset.x, offset.y) { drawHole(bounds) } - } - } + val offset = offset() + translate(offset.x, offset.y) { drawHole(holeSize) } } - private fun DrawScope.drawHole(bounds: Element) { - val boundsSize = bounds.lastSharedState.size.toSize() + private fun DrawScope.drawHole(size: Size) { if (shape == RectangleShape) { - drawRect(Color.Black, size = boundsSize, blendMode = BlendMode.DstOut) + drawRect(Color.Black, size = size, blendMode = BlendMode.DstOut) return } val outline = - if (boundsSize == lastSize && layoutDirection == lastLayoutDirection) { + if (size == lastSize && layoutDirection == lastLayoutDirection) { lastOutline!! } else { - val newOutline = shape.createOutline(boundsSize, layoutDirection, this) - lastSize = boundsSize + val newOutline = shape().createOutline(size, layoutDirection, this) + lastSize = size lastLayoutDirection = layoutDirection lastOutline = newOutline newOutline @@ -118,3 +143,39 @@ private class PunchHoleNode( ) } } + +private data class PunchHoleWithBoundsElement( + private val coords: () -> LayoutCoordinates?, + private val shape: Shape, +) : ModifierNodeElement<PunchHoleWithBoundsNode>() { + override fun create(): PunchHoleWithBoundsNode = PunchHoleWithBoundsNode(coords, shape) + + override fun update(node: PunchHoleWithBoundsNode) { + node.holeCoords = coords + node.shape = shape + } +} + +private class PunchHoleWithBoundsNode( + var holeCoords: () -> LayoutCoordinates?, + var shape: Shape, +) : DelegatingNode(), DrawModifierNode, GlobalPositionAwareModifierNode { + private val delegate = delegate(PunchHoleNode(::holeSize, ::holeOffset, ::shape)) + private var lastCoords: LayoutCoordinates? = null + + override fun onGloballyPositioned(coordinates: LayoutCoordinates) { + this.lastCoords = coordinates + } + + override fun ContentDrawScope.draw() = with(delegate) { draw() } + + private fun holeSize(): Size { + return holeCoords()?.size?.toSize() ?: Size.Zero + } + + private fun holeOffset(): Offset { + val holeCoords = holeCoords() ?: return Offset.Zero + val lastCoords = lastCoords ?: error("draw() was called before onGloballyPositioned()") + return lastCoords.localPositionOf(holeCoords, relativeToSource = Offset.Zero) + } +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt index 3537b7989ed5..f67df54b088c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt @@ -26,7 +26,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.intermediateLayout import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.IntSize @@ -139,12 +138,6 @@ internal class SceneScopeImpl( bottomOrRightBehavior = bottomBehavior, ) - override fun Modifier.punchHole( - element: ElementKey, - bounds: ElementKey, - shape: Shape - ): Modifier = punchHole(layoutImpl, element, bounds, shape) - override fun Modifier.noResizeDuringTransitions(): Modifier { return noResizeDuringTransitions(layoutState = layoutImpl.state) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index 338557d0942e..64388b7653e0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -312,13 +312,16 @@ internal class SceneGestureHandler( // immediately go back B => A. if (targetScene != swipeTransition._currentScene) { swipeTransition._currentScene = targetScene - layoutImpl.onChangeScene(targetScene.key) + with(layoutImpl.state) { coroutineScope.onChangeScene(targetScene.key) } } - animateOffset( + swipeTransition.animateOffset( + coroutineScope = coroutineScope, initialVelocity = velocity, targetOffset = targetOffset, - targetScene = targetScene.key + onAnimationCompleted = { + layoutState.finishTransition(swipeTransition, idleScene = targetScene.key) + } ) } @@ -410,34 +413,6 @@ internal class SceneGestureHandler( } } - private fun animateOffset( - initialVelocity: Float, - targetOffset: Float, - targetScene: SceneKey, - ) { - swipeTransition.startOffsetAnimation { - coroutineScope.launch { - if (!swipeTransition.isAnimatingOffset) { - swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) - } - swipeTransition.isAnimatingOffset = true - - swipeTransition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) - - swipeTransition.finishOffsetAnimation() - layoutState.finishTransition(swipeTransition, targetScene) - } - } - } - internal class SwipeTransition( val _fromScene: Scene, val _toScene: Scene, @@ -479,12 +454,14 @@ internal class SceneGestureHandler( private var offsetAnimationJob: Job? = null /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ - fun startOffsetAnimation(job: () -> Job) { + private fun startOffsetAnimation(job: () -> Job) { cancelOffsetAnimation() offsetAnimationJob = job() } /** Cancel any ongoing offset animation. */ + // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at + // the same time. fun cancelOffsetAnimation() { offsetAnimationJob?.cancel() finishOffsetAnimation() @@ -496,6 +473,43 @@ internal class SceneGestureHandler( dragOffset = offsetAnimatable.value } } + + // TODO(b/290184746): Make this spring spec configurable. + private val animationSpec = + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ) + + fun animateOffset( + // TODO(b/317063114) The CoroutineScope should be removed. + coroutineScope: CoroutineScope, + initialVelocity: Float, + targetOffset: Float, + onAnimationCompleted: () -> Unit, + ) { + startOffsetAnimation { + coroutineScope.launch { + animateOffset(targetOffset, initialVelocity) + onAnimationCompleted() + } + } + } + + private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) { + if (!isAnimatingOffset) { + offsetAnimatable.snapTo(dragOffset) + } + isAnimatingOffset = true + + offsetAnimatable.animateTo( + targetValue = targetOffset, + animationSpec = animationSpec, + initialVelocity = initialVelocity, + ) + + finishOffsetAnimation() + } } companion object { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 84fade8937ff..80f8c1c9e987 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -19,17 +19,48 @@ package com.android.compose.animation.scene import androidx.annotation.FloatRange import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.platform.LocalDensity -import kotlinx.coroutines.channels.Channel + +/** + * [SceneTransitionLayout] is a container that automatically animates its content whenever its state + * changes. + * + * Note: You should use [androidx.compose.animation.AnimatedContent] instead of + * [SceneTransitionLayout] if it fits your need. Use [SceneTransitionLayout] over AnimatedContent if + * you need support for swipe gestures, shared elements or transitions defined declaratively outside + * UI code. + * + * @param state the state of this layout. + * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any. + * @param transitionInterceptionThreshold used during a scene transition. For the scene to be + * intercepted, the progress value must be above the threshold, and below (1 - threshold). + * @param scenes the configuration of the different scenes of this layout. + * @see updateSceneTransitionLayoutState + */ +@Composable +fun SceneTransitionLayout( + state: SceneTransitionLayoutState, + modifier: Modifier = Modifier, + edgeDetector: EdgeDetector = DefaultEdgeDetector, + @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f, + scenes: SceneTransitionLayoutScope.() -> Unit, +) { + SceneTransitionLayoutForTesting( + state, + modifier, + edgeDetector, + transitionInterceptionThreshold, + onLayoutImpl = null, + scenes, + ) +} /** * [SceneTransitionLayout] is a container that automatically animates its content whenever @@ -45,7 +76,6 @@ import kotlinx.coroutines.channels.Channel * This is called when the user commits a transition to a new scene because of a [UserAction], for * instance by triggering back navigation or by swiping to a new scene. * @param transitions the definition of the transitions used to animate a change of scene. - * @param state the observable state of this layout. * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any. * @param transitionInterceptionThreshold used during a scene transition. For the scene to be * intercepted, the progress value must be above the threshold, and below (1 - threshold). @@ -57,20 +87,16 @@ fun SceneTransitionLayout( onChangeScene: (SceneKey) -> Unit, transitions: SceneTransitions, modifier: Modifier = Modifier, - state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) }, edgeDetector: EdgeDetector = DefaultEdgeDetector, @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f, scenes: SceneTransitionLayoutScope.() -> Unit, ) { - SceneTransitionLayoutForTesting( - currentScene, - onChangeScene, - modifier, - transitions, + val state = updateSceneTransitionLayoutState(currentScene, onChangeScene, transitions) + SceneTransitionLayout( state, + modifier, edgeDetector, transitionInterceptionThreshold, - onLayoutImpl = null, scenes, ) } @@ -203,18 +229,6 @@ interface BaseSceneScope { ): Modifier /** - * Punch a hole in this [element] using the bounds of [bounds] in [scene] and the given [shape]. - * - * Punching a hole in an element will "remove" any pixel drawn by that element in the hole area. - * This can be used to make content drawn below an opaque element visible. For example, if we - * have [this lockscreen scene](http://shortn/_VYySFnJDhN) drawn below - * [this shade scene](http://shortn/_fpxGUk0Rg7) and punch a hole in the latter using the big - * clock time bounds and a RoundedCornerShape(10dp), [this](http://shortn/_qt80IvORFj) would be - * the result. - */ - fun Modifier.punchHole(element: ElementKey, bounds: ElementKey, shape: Shape): Modifier - - /** * Don't resize during transitions. This can for instance be used to make sure that scrollable * lists keep a constant size during transitions even if its elements are growing/shrinking. */ @@ -346,11 +360,8 @@ enum class SwipeDirection(val orientation: Orientation) { */ @Composable internal fun SceneTransitionLayoutForTesting( - currentScene: SceneKey, - onChangeScene: (SceneKey) -> Unit, + state: SceneTransitionLayoutState, modifier: Modifier = Modifier, - transitions: SceneTransitions = transitions {}, - state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) }, edgeDetector: EdgeDetector = DefaultEdgeDetector, transitionInterceptionThreshold: Float = 0f, onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null, @@ -360,8 +371,7 @@ internal fun SceneTransitionLayoutForTesting( val coroutineScope = rememberCoroutineScope() val layoutImpl = remember { SceneTransitionLayoutImpl( - state = state as SceneTransitionLayoutStateImpl, - onChangeScene = onChangeScene, + state = state as BaseSceneTransitionLayoutState, density = density, edgeDetector = edgeDetector, transitionInterceptionThreshold = transitionInterceptionThreshold, @@ -375,7 +385,6 @@ internal fun SceneTransitionLayoutForTesting( // SnapshotStateMap anymore. layoutImpl.updateScenes(scenes) - val targetSceneChannel = remember { Channel<SceneKey>(Channel.CONFLATED) } SideEffect { if (state != layoutImpl.state) { error( @@ -384,23 +393,8 @@ internal fun SceneTransitionLayoutForTesting( ) } - layoutImpl.onChangeScene = onChangeScene - (state as SceneTransitionLayoutStateImpl).transitions = transitions layoutImpl.density = density layoutImpl.edgeDetector = edgeDetector - - state.transitions = transitions - - targetSceneChannel.trySend(currentScene) - } - - LaunchedEffect(targetSceneChannel) { - for (newKey in targetSceneChannel) { - // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame - // late. - val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey - animateToScene(layoutImpl.state, newKey) - } } layoutImpl.Content(modifier) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 0227aba94b53..7cc9d2623e9c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -20,14 +20,11 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.key import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.layout.LookaheadScope import androidx.compose.ui.layout.intermediateLayout import androidx.compose.ui.unit.Density @@ -48,13 +45,12 @@ internal typealias MovableElementContent = @Stable internal class SceneTransitionLayoutImpl( - internal val state: SceneTransitionLayoutStateImpl, - internal var onChangeScene: (SceneKey) -> Unit, + internal val state: BaseSceneTransitionLayoutState, internal var density: Density, internal var edgeDetector: EdgeDetector, internal var transitionInterceptionThreshold: Float, builder: SceneTransitionLayoutScope.() -> Unit, - coroutineScope: CoroutineScope, + private val coroutineScope: CoroutineScope, ) { /** * The map of [Scene]s. @@ -100,16 +96,6 @@ internal class SceneTransitionLayoutImpl( ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>() .also { _sharedValues = it } - /** - * The scenes that are "ready", i.e. they were composed and fully laid-out at least once. - * - * Note that this map is *read* during composition, so it is a [SnapshotStateMap] to make sure - * that we recompose when modifications are made to this map. - * - * TODO(b/316901148): Remove this map. - */ - private val readyScenes = SnapshotStateMap<SceneKey, Boolean>() - private val horizontalGestureHandler: SceneGestureHandler private val verticalGestureHandler: SceneGestureHandler @@ -244,49 +230,19 @@ internal class SceneTransitionLayoutImpl( // TODO(b/290184746): Make sure that this works with SystemUI once we use // SceneTransitionLayout in Flexiglass. scene(state.transitionState.currentScene).userActions[Back]?.let { backScene -> - BackHandler { onChangeScene(backScene) } + BackHandler { with(state) { coroutineScope.onChangeScene(backScene) } } } Box { scenesToCompose.fastForEach { scene -> val key = scene.key - key(key) { - // Mark this scene as ready once it has been composed, laid out and - // drawn the first time. We have to do this in a LaunchedEffect here - // because DisposableEffect runs between composition and layout. - LaunchedEffect(key) { readyScenes[key] = true } - DisposableEffect(key) { onDispose { readyScenes.remove(key) } } - - scene.Content( - Modifier.drawWithContent { - if (state.currentTransition == null) { - drawContent() - } else { - // Don't draw scenes that are not ready yet. - if (readyScenes.containsKey(key)) { - drawContent() - } - } - } - ) - } + key(key) { scene.Content() } } } } } } - /** - * Return whether [transition] is ready, i.e. the elements of both scenes of the transition were - * laid out at least once. - */ - internal fun isTransitionReady(transition: TransitionState.Transition): Boolean { - return readyScenes.containsKey(transition.fromScene) && - readyScenes.containsKey(transition.toScene) - } - - internal fun isSceneReady(scene: SceneKey): Boolean = readyScenes.containsKey(scene) - internal fun setScenesTargetSizeForTest(size: IntSize) { scenes.values.forEach { it.targetSize = size } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 0607aa148157..956e326dc03e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -16,12 +16,23 @@ package com.android.compose.animation.scene +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel -/** The state of a [SceneTransitionLayout]. */ +/** + * The state of a [SceneTransitionLayout]. + * + * @see MutableSceneTransitionLayoutState + * @see updateSceneTransitionLayoutState + */ @Stable sealed interface SceneTransitionLayoutState { /** @@ -36,6 +47,9 @@ sealed interface SceneTransitionLayoutState { val currentTransition: TransitionState.Transition? get() = transitionState as? TransitionState.Transition + /** The [SceneTransitions] used when animating this state. */ + val transitions: SceneTransitions + /** * Whether we are transitioning. If [from] or [to] is empty, we will also check that they match * the scenes we are animating from and/or to. @@ -46,9 +60,68 @@ sealed interface SceneTransitionLayoutState { fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean } -/** Create a new [SceneTransitionLayoutState] that is currently idle at scene [currentScene]. */ -fun SceneTransitionLayoutState(currentScene: SceneKey): SceneTransitionLayoutState { - return SceneTransitionLayoutStateImpl(currentScene, SceneTransitions.Empty) +/** A [SceneTransitionLayoutState] whose target scene can be imperatively set. */ +sealed interface MutableSceneTransitionLayoutState : SceneTransitionLayoutState { + /** The [SceneTransitions] used when animating this state. */ + override var transitions: SceneTransitions + + /** + * Set the target scene of this state to [targetScene]. + * + * If [targetScene] is the same as the [currentScene][TransitionState.currentScene] of + * [transitionState], then nothing will happen and this will return `null`. Note that this means + * that this will also do nothing if the user is currently swiping from [targetScene] to another + * scene, or if we were already animating to [targetScene]. + * + * If [targetScene] is different than the [currentScene][TransitionState.currentScene] of + * [transitionState], then this will animate to [targetScene]. The associated + * [TransitionState.Transition] will be returned and will be set as the current + * [transitionState] of this [MutableSceneTransitionLayoutState]. + * + * Note that because a non-null [TransitionState.Transition] is returned does not mean that the + * transition will finish and that we will settle to [targetScene]. The returned transition + * might still be interrupted, for instance by another call to [setTargetScene] or by a user + * gesture. + * + * If [this] [CoroutineScope] is cancelled during the transition and that the transition was + * still active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be + * set to `TransitionState.Idle(targetScene)`. + * + * TODO(b/318794193): Add APIs to await() and cancel() any [TransitionState.Transition]. + */ + fun setTargetScene( + targetScene: SceneKey, + coroutineScope: CoroutineScope, + ): TransitionState.Transition? +} + +/** Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene]. */ +fun MutableSceneTransitionLayoutState( + initialScene: SceneKey, + transitions: SceneTransitions = SceneTransitions.Empty, +): MutableSceneTransitionLayoutState { + return MutableSceneTransitionLayoutStateImpl(initialScene, transitions) +} + +/** + * Sets up a [SceneTransitionLayoutState] and keeps it synced with [currentScene], [onChangeScene] + * and [transitions]. New transitions will automatically be started whenever [currentScene] is + * changed. + * + * @param currentScene the current scene + * @param onChangeScene a mutator that should set [currentScene] to the given scene when called. + * This is called when the user commits a transition to a new scene because of a [UserAction], for + * instance by triggering back navigation or by swiping to a new scene. + * @param transitions the definition of the transitions used to animate a change of scene. + */ +@Composable +fun updateSceneTransitionLayoutState( + currentScene: SceneKey, + onChangeScene: (SceneKey) -> Unit, + transitions: SceneTransitions = SceneTransitions.Empty, +): SceneTransitionLayoutState { + return remember { HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene) } + .apply { update(currentScene, onChangeScene, transitions) } } @Stable @@ -109,13 +182,11 @@ sealed interface TransitionState { } } -internal class SceneTransitionLayoutStateImpl( - initialScene: SceneKey, - internal var transitions: SceneTransitions, -) : SceneTransitionLayoutState { +internal abstract class BaseSceneTransitionLayoutState(initialScene: SceneKey) : + SceneTransitionLayoutState { override var transitionState: TransitionState by mutableStateOf(TransitionState.Idle(initialScene)) - private set + protected set /** * The current [transformationSpec] associated to [transitionState]. Accessing this value makes @@ -123,6 +194,14 @@ internal class SceneTransitionLayoutStateImpl( */ internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty + /** + * Called when the [current scene][TransitionState.currentScene] should be changed to [scene]. + * + * When this is called, the source of truth for the current scene should be changed so that + * [transitionState] will animate and settle to [scene]. + */ + internal abstract fun CoroutineScope.onChangeScene(scene: SceneKey) + override fun isTransitioning(from: SceneKey?, to: SceneKey?): Boolean { val transition = currentTransition ?: return false return transition.isTransitioning(from, to) @@ -154,3 +233,62 @@ internal class SceneTransitionLayoutStateImpl( } } } + +/** + * A [SceneTransitionLayout] whose current scene/source of truth is hoisted (its current value comes + * from outside). + */ +internal class HoistedSceneTransitionLayoutScene( + initialScene: SceneKey, + override var transitions: SceneTransitions, + private var changeScene: (SceneKey) -> Unit, +) : BaseSceneTransitionLayoutState(initialScene) { + private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED) + + override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene(scene) + + @Composable + fun update( + currentScene: SceneKey, + onChangeScene: (SceneKey) -> Unit, + transitions: SceneTransitions, + ) { + SideEffect { + this.changeScene = onChangeScene + this.transitions = transitions + + targetSceneChannel.trySend(currentScene) + } + + LaunchedEffect(targetSceneChannel) { + for (newKey in targetSceneChannel) { + // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame + // late. + val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey + animateToScene(layoutState = this@HoistedSceneTransitionLayoutScene, newKey) + } + } + } +} + +/** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */ +internal class MutableSceneTransitionLayoutStateImpl( + initialScene: SceneKey, + override var transitions: SceneTransitions, +) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene) { + override fun setTargetScene( + targetScene: SceneKey, + coroutineScope: CoroutineScope + ): TransitionState.Transition? { + return with(this) { + coroutineScope.animateToScene( + layoutState = this@MutableSceneTransitionLayoutStateImpl, + target = targetScene, + ) + } + } + + override fun CoroutineScope.onChangeScene(scene: SceneKey) { + setTargetScene(scene, coroutineScope = this) + } +} diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 35a5054cbd2a..c0de87abbfe8 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -16,7 +16,6 @@ package com.android.compose.animation.scene -import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box @@ -35,17 +34,11 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.layout.intermediateLayout -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.compose.test.subjects.DpOffsetSubject -import com.android.compose.test.subjects.assertThat import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -263,8 +256,11 @@ class ElementTest { rule.setContent { SceneTransitionLayoutForTesting( - currentScene = currentScene, - onChangeScene = { currentScene = it }, + state = + updateSceneTransitionLayoutState( + currentScene = currentScene, + onChangeScene = { currentScene = it } + ), onLayoutImpl = { nullableLayoutImpl = it }, ) { scene(TestScenes.SceneA) { /* Nothing */} @@ -428,8 +424,11 @@ class ElementTest { rule.setContent { SceneTransitionLayoutForTesting( - currentScene = TestScenes.SceneA, - onChangeScene = {}, + state = + updateSceneTransitionLayoutState( + currentScene = TestScenes.SceneA, + onChangeScene = {} + ), onLayoutImpl = { nullableLayoutImpl = it }, ) { scene(TestScenes.SceneA) { Box(Modifier.element(key)) } @@ -478,8 +477,11 @@ class ElementTest { scrollScope = rememberCoroutineScope() SceneTransitionLayoutForTesting( - currentScene = TestScenes.SceneA, - onChangeScene = {}, + state = + updateSceneTransitionLayoutState( + currentScene = TestScenes.SceneA, + onChangeScene = {} + ), onLayoutImpl = { nullableLayoutImpl = it }, ) { scene(TestScenes.SceneA) { @@ -565,86 +567,4 @@ class ElementTest { after { assertThat(fooCompositions).isEqualTo(1) } } } - - @Test - fun sharedElementOffsetIsUpdatedEvenWhenNotPlaced() { - var nullableLayoutImpl: SceneTransitionLayoutImpl? = null - var density: Density? = null - - fun layoutImpl() = nullableLayoutImpl ?: error("nullableLayoutImpl was not set") - - fun density() = density ?: error("density was not set") - - fun Offset.toDpOffset() = with(density()) { DpOffset(x.toDp(), y.toDp()) } - - fun foo() = layoutImpl().elements[TestElements.Foo] ?: error("Foo not in elements map") - - fun Element.lastSharedOffset() = lastSharedState.offset.toDpOffset() - - fun Element.lastOffsetIn(scene: SceneKey) = - (sceneStates[scene] ?: error("$scene not in sceneValues map")) - .lastState - .offset - .toDpOffset() - - rule.testTransition( - from = TestScenes.SceneA, - to = TestScenes.SceneB, - transitionLayout = { currentScene, onChangeScene -> - density = LocalDensity.current - - SceneTransitionLayoutForTesting( - currentScene = currentScene, - onChangeScene = onChangeScene, - onLayoutImpl = { nullableLayoutImpl = it }, - transitions = - transitions { - from(TestScenes.SceneA, to = TestScenes.SceneB) { - spec = tween(durationMillis = 4 * 16, easing = LinearEasing) - } - } - ) { - scene(TestScenes.SceneA) { Box(Modifier.element(TestElements.Foo)) } - scene(TestScenes.SceneB) { - Box(Modifier.offset(x = 40.dp, y = 80.dp).element(TestElements.Foo)) - } - } - } - ) { - val tolerance = DpOffsetSubject.DefaultTolerance - - before { - val expected = DpOffset(0.dp, 0.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - } - - at(16) { - val expected = DpOffset(10.dp, 20.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - - at(32) { - val expected = DpOffset(20.dp, 40.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - - at(48) { - val expected = DpOffset(30.dp, 60.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - - after { - val expected = DpOffset(40.dp, 80.dp) - assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected) - assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected) - } - } - } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt index 04b3f8a1dfe7..0f9b0249b93f 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt @@ -32,7 +32,7 @@ class ObservableTransitionStateTest { @Test fun testObservableTransitionState() = runTest { - val state = SceneTransitionLayoutState(TestScenes.SceneA) + lateinit var state: SceneTransitionLayoutState // Collect the current observable state into [observableState]. // TODO(b/290184746): Use collectValues {} once it is extracted into a library that can be @@ -58,12 +58,14 @@ class ObservableTransitionStateTest { from = TestScenes.SceneA, to = TestScenes.SceneB, transitionLayout = { currentScene, onChangeScene -> - SceneTransitionLayout( - currentScene, - onChangeScene, - EmptyTestTransitions, - state = state, - ) { + state = + updateSceneTransitionLayoutState( + currentScene, + onChangeScene, + EmptyTestTransitions + ) + + SceneTransitionLayout(state = state) { scene(TestScenes.SceneA) {} scene(TestScenes.SceneB) {} } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index d9ce5191f3d9..066a3e45fb3c 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -18,9 +18,6 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation import androidx.compose.material3.Text -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource @@ -53,10 +50,8 @@ class SceneGestureHandlerTest { private class TestGestureScope( val coroutineScope: MonotonicClockTestScope, ) { - private var internalCurrentScene: SceneKey by mutableStateOf(SceneA) - private val layoutState = - SceneTransitionLayoutStateImpl(internalCurrentScene, EmptyTestTransitions) + MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions) val mutableUserActionsA: MutableMap<UserAction, SceneKey> = mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC) @@ -94,7 +89,6 @@ class SceneGestureHandlerTest { private val layoutImpl = SceneTransitionLayoutImpl( state = layoutState, - onChangeScene = { internalCurrentScene = it }, density = Density(1f), edgeDetector = DefaultEdgeDetector, transitionInterceptionThreshold = transitionInterceptionThreshold, diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index 75dee47a91cd..48825fb88096 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -18,7 +18,11 @@ package com.android.compose.animation.scene import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.test.runMonotonicClockTest import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -29,7 +33,7 @@ class SceneTransitionLayoutStateTest { @Test fun isTransitioningTo_idle() { - val state = SceneTransitionLayoutState(TestScenes.SceneA) + val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty) assertThat(state.isTransitioning()).isFalse() assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse() @@ -40,7 +44,7 @@ class SceneTransitionLayoutStateTest { @Test fun isTransitioningTo_transition() { - val state = SceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty) + val state = MutableSceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty) state.startTransition(transition(from = TestScenes.SceneA, to = TestScenes.SceneB)) assertThat(state.isTransitioning()).isTrue() @@ -50,4 +54,56 @@ class SceneTransitionLayoutStateTest { assertThat(state.isTransitioning(to = TestScenes.SceneA)).isFalse() assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue() } + + @Test + fun setTargetScene_idleToSameScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + assertThat(state.setTargetScene(TestScenes.SceneA, coroutineScope = this)).isNull() + } + + @Test + fun setTargetScene_idleToDifferentScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + val transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this) + assertThat(transition).isNotNull() + assertThat(state.transitionState).isEqualTo(transition) + + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB)) + } + + @Test + fun setTargetScene_transitionToSameScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull() + assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNull() + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB)) + } + + @Test + fun setTargetScene_transitionToDifferentScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull() + assertThat(state.setTargetScene(TestScenes.SceneC, coroutineScope = this)).isNotNull() + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneC)) + } + + @Test + fun setTargetScene_coroutineScopeCancelled() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + + lateinit var transition: TransitionState.Transition + val job = + launch(start = CoroutineStart.UNDISPATCHED) { + transition = state.setTargetScene(TestScenes.SceneB, coroutineScope = this)!! + } + assertThat(state.transitionState).isEqualTo(transition) + + // Cancelling the scope/job still sets the state to Idle(targetScene). + job.cancel() + testScheduler.advanceUntilIdle() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneB)) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt index 649e4991434e..efaea71f8d2c 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt @@ -63,7 +63,7 @@ class SceneTransitionLayoutTest { } private var currentScene by mutableStateOf(TestScenes.SceneA) - private val layoutState = SceneTransitionLayoutState(currentScene) + private lateinit var layoutState: SceneTransitionLayoutState // We use createAndroidComposeRule() here and not createComposeRule() because we need an // activity for testBack(). @@ -72,10 +72,14 @@ class SceneTransitionLayoutTest { /** The content under test. */ @Composable private fun TestContent() { + layoutState = + updateSceneTransitionLayoutState( + currentScene, + { currentScene = it }, + EmptyTestTransitions + ) + SceneTransitionLayout( - currentScene, - { currentScene = it }, - EmptyTestTransitions, state = layoutState, modifier = Modifier.size(LayoutSize), ) { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 58d853ef5a00..1ec3c8ba2301 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -20,9 +20,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.LocalViewConfiguration @@ -58,18 +55,15 @@ class SwipeToSceneTest { get() = Offset(0f, (LayoutHeight / 2).toPx()) } - private var currentScene by mutableStateOf(TestScenes.SceneA) - private val layoutState = SceneTransitionLayoutState(currentScene) - @get:Rule val rule = createComposeRule() + private fun layoutState(initialScene: SceneKey = TestScenes.SceneA) = + MutableSceneTransitionLayoutState(initialScene, EmptyTestTransitions) + /** The content under test. */ @Composable - private fun TestContent() { + private fun TestContent(layoutState: SceneTransitionLayoutState) { SceneTransitionLayout( - currentScene, - { currentScene = it }, - EmptyTestTransitions, state = layoutState, modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName), ) { @@ -109,9 +103,11 @@ class SwipeToSceneTest { // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f + + val layoutState = layoutState() rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) @@ -195,9 +191,10 @@ class SwipeToSceneTest { // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f + val layoutState = layoutState() rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) @@ -260,14 +257,14 @@ class SwipeToSceneTest { @Test fun multiPointerSwipe() { // Start at scene C. - currentScene = TestScenes.SceneC + val layoutState = layoutState(TestScenes.SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) @@ -299,14 +296,14 @@ class SwipeToSceneTest { @Test fun defaultEdgeSwipe() { // Start at scene C. - currentScene = TestScenes.SceneC + val layoutState = layoutState(TestScenes.SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop - TestContent() + TestContent(layoutState) } assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt index cb122dc8e25e..fbcd5b27836e 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.withContext * * The [TestCoroutineScheduler] is passed to provide the functionality to wait for idle. */ -@ExperimentalTestApi +@OptIn(ExperimentalTestApi::class) fun runMonotonicClockTest(block: suspend MonotonicClockTestScope.() -> Unit) = runTest { // We need a CoroutineScope (like a TestScope) to create a TestMonotonicFrameClock. withContext(TestMonotonicFrameClock(this)) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 41bde5298c66..3dfe65a4f736 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -22,6 +22,7 @@ import android.net.Uri import android.os.UserHandle import android.provider.Settings import androidx.annotation.OpenForTesting +import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.log.core.LogcatOnlyMessageBuffer import com.android.systemui.log.core.Logger @@ -120,8 +121,9 @@ open class ClockRegistry( override fun onPluginAttached( manager: PluginLifecycleManager<ClockProviderPlugin> ): Boolean { - manager.isDebug = !keepAllLoaded - + manager.setLogFunc({ tag, msg -> + (clockBuffers?.infraMessageBuffer as LogBuffer?)?.log(tag, LogLevel.DEBUG, msg) + }) if (keepAllLoaded) { // Always load new plugins if requested return true diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index c8461d2c5415..02d30c5ea20a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -197,7 +197,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(true) featureFlags = FakeFeatureFlags() - featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false) featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index cbcca55f6a34..f7751753cc18 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -100,9 +100,12 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { mSelectedUserInteractor ) underTest.init() + underTest.onViewAttached() underTest.onResume(0) verify(keyguardUpdateMonitor) .registerCallback(updateMonitorCallbackArgumentCaptor.capture()) + reset(keyguardMessageAreaController) + reset(keyguardUpdateMonitor) } @Test @@ -110,25 +113,24 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { underTest.onViewAttached() verify(keyguardMessageAreaController) .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) + verify(keyguardUpdateMonitor) + .registerCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test fun onViewDetached() { underTest.onViewDetached() + verify(keyguardUpdateMonitor).removeCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test fun onResume() { - reset(keyguardUpdateMonitor) underTest.onResume(KeyguardSecurityView.VIEW_REVEALED) - verify(keyguardUpdateMonitor) - .registerCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test fun onPause() { underTest.onPause() - verify(keyguardUpdateMonitor).removeCallback(any(KeyguardUpdateMonitorCallback::class.java)) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt new file mode 100644 index 000000000000..721fc4906aba --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2024 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.communal.log + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory +import com.android.systemui.communal.shared.log.CommunalUiEvent +import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow +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.Mockito.any +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +class CommunalLoggerStartableTest : SysuiTestCase() { + @Mock private lateinit var uiEventLogger: UiEventLogger + + private lateinit var testScope: TestScope + private lateinit var communalInteractor: CommunalInteractor + private lateinit var underTest: CommunalLoggerStartable + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + val withDeps = CommunalInteractorFactory.create() + testScope = withDeps.testScope + communalInteractor = withDeps.communalInteractor + + underTest = + CommunalLoggerStartable( + testScope.backgroundScope, + communalInteractor, + uiEventLogger, + ) + underTest.start() + } + + @Test + fun transitionStateLogging_enterCommunalHub() = + testScope.runTest { + // Transition state is default (non-communal) + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify nothing is logged from the default state + verify(uiEventLogger, never()).log(any()) + + // Start transition to communal + transitionState.value = transition(to = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START) + + // Finish transition to communal + transitionState.value = idle(CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH) + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + } + + @Test + fun transitionStateLogging_enterCommunalHub_canceled() = + testScope.runTest { + // Transition state is default (non-communal) + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify nothing is logged from the default state + verify(uiEventLogger, never()).log(any()) + + // Start transition to communal + transitionState.value = transition(to = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START) + + // Cancel the transition + transitionState.value = idle(CommunalSceneKey.DEFAULT) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL) + + // Verify neither SHOWN nor GONE is logged + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + @Test + fun transitionStateLogging_exitCommunalHub() = + testScope.runTest { + // Transition state is communal + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify SHOWN is logged when it's the default state + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + + // Start transition from communal + transitionState.value = transition(from = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START) + + // Finish transition to communal + transitionState.value = idle(CommunalSceneKey.DEFAULT) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH) + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + @Test + fun transitionStateLogging_exitCommunalHub_canceled() = + testScope.runTest { + // Transition state is communal + val transitionState = + MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal)) + communalInteractor.setTransitionState(transitionState) + runCurrent() + + // Clear the initial SHOWN event from the logger + clearInvocations(uiEventLogger) + + // Start transition from communal + transitionState.value = transition(from = CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START) + + // Cancel the transition + transitionState.value = idle(CommunalSceneKey.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL) + + // Verify neither SHOWN nor GONE is logged + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + private fun transition( + from: CommunalSceneKey = CommunalSceneKey.DEFAULT, + to: CommunalSceneKey = CommunalSceneKey.DEFAULT, + ): ObservableCommunalTransitionState.Transition { + return ObservableCommunalTransitionState.Transition( + fromScene = from, + toScene = to, + progress = emptyFlow(), + isInitiatedByUserInput = true, + isUserInputOngoing = emptyFlow(), + ) + } + + private fun idle(sceneKey: CommunalSceneKey): ObservableCommunalTransitionState.Idle { + return ObservableCommunalTransitionState.Idle(sceneKey) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index ff6fd43745df..54510a82201a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -21,7 +21,6 @@ import android.app.Activity.RESULT_OK import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetHost import android.content.ComponentName -import android.os.PowerManager import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -41,13 +40,11 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -64,8 +61,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class CommunalEditModeViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost - @Mock private lateinit var shadeViewController: ShadeViewController - @Mock private lateinit var powerManager: PowerManager @Mock private lateinit var appWidgetHost: AppWidgetHost @Mock private lateinit var uiEventLogger: UiEventLogger @@ -97,8 +92,6 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalEditModeViewModel( withDeps.communalInteractor, appWidgetHost, - Provider { shadeViewController }, - powerManager, mediaHost, uiEventLogger, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 8e3f66464de6..a7760621e97c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget -import android.os.PowerManager import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -36,12 +35,10 @@ import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy @@ -58,8 +55,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class CommunalViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost - @Mock private lateinit var shadeViewController: ShadeViewController - @Mock private lateinit var powerManager: PowerManager private lateinit var testScope: TestScope @@ -92,8 +87,6 @@ class CommunalViewModelTest : SysuiTestCase() { withDeps.communalInteractor, WidgetInteractionHandler(mock()), withDeps.tutorialInteractor, - Provider { shadeViewController }, - powerManager, mediaHost, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index 3d1efa59a11b..5827671e22b8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -293,8 +293,8 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { } /** - * Verifies that swiping up when the lock pattern is not secure does not consume the scroll - * gesture or expand. + * Verifies that swiping up when the lock pattern is not secure dismissed dream and consumes + * the gesture. */ @Test public void testSwipeUp_keyguardNotSecure_doesNotExpand() { @@ -314,11 +314,44 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { reset(mScrimController); + // Scroll gesture is consumed. + assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) + .isTrue(); + // We should not expand since the keyguard is not secure + verify(mScrimController, never()).expand(any()); + // Since we are swiping up, we should wake from dreams. + verify(mCentralSurfaces).awakenDreams(); + } + + /** + * Verifies that swiping down when the lock pattern is not secure does not dismiss the dream. + */ + @Test + public void testSwipeDown_keyguardNotSecure_doesNotExpand() { + when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); + mTouchHandler.onSessionStart(mTouchSession); + ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); + + final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); + + final float distanceY = SCREEN_HEIGHT_PX * 0.3f; + // Swiping down near the bottom of the screen where the touch initiation region is. + final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX - distanceY, 0); + final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX, 0); + + reset(mScrimController); + // Scroll gesture is not consumed. assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) - .isFalse(); + .isTrue(); // We should not expand since the keyguard is not secure verify(mScrimController, never()).expand(any()); + // Since we are swiping down, we should not dismiss the dream. + verify(mCentralSurfaces, never()).awakenDreams(); } private void verifyScroll(float percent, Direction direction, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt index b483085cf1e5..9bccf4e26e87 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt @@ -82,6 +82,7 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { keyguardTransitionInteractor, fakeLightRevealScrimRepository, testScope.backgroundScope, + mock(), mock() ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt index e7037a682cca..9daf1860ebb8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt @@ -94,7 +94,7 @@ class AlternateBouncerToAodTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(5) + assertThat(values.size).isEqualTo(6) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt index e141c2b3107f..f1690dafe75a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt @@ -95,7 +95,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(6) + assertThat(values.size).isEqualTo(7) values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) } } @@ -117,7 +117,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(3) + assertThat(values.size).isEqualTo(4) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } @@ -232,7 +232,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt index 897ce6d305b6..f763a6790b51 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt @@ -59,9 +59,9 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() { testScope, ) - // Only three values should be present, since the dream overlay runs for a small + // Only five values should be present, since the dream overlay runs for a small // fraction of the overall animation time - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } @@ -84,7 +84,7 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt index 4843f8ba4249..74025fd6e100 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -73,9 +73,9 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { testScope = testScope, ) - // Only three values should be present, since the dream overlay runs for a small + // Only five values should be present, since the dream overlay runs for a small // fraction of the overall animation time - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } @@ -98,10 +98,10 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { testScope = testScope, ) - assertThat(values.size).isEqualTo(5) + assertThat(values.size).isEqualTo(6) values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) } // Validate finished value - assertThat(values[4]).isEqualTo(0f) + assertThat(values[5]).isEqualTo(0f) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt index a1b8aab402a7..6fcb0c11edad 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -74,9 +74,9 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { ), testScope = testScope, ) - // Only 3 values should be present, since the dream overlay runs for a small fraction + // Only 5 values should be present, since the dream overlay runs for a small fraction // of the overall animation time - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt index 2111ad5d975e..639114c15000 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.testKosmos import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -54,6 +55,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { fun lockscreenFadeIn() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( @@ -83,6 +85,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { 100 ) val values by collectValues(underTest.lockscreenTranslationY) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( @@ -95,7 +98,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(4) + assertThat(values.size).isEqualTo(5) values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) } } @@ -107,6 +110,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { 100 ) val values by collectValues(underTest.lockscreenTranslationY) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0.5f, TransitionState.CANCELED)) @@ -117,6 +121,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() { fun deviceEntryParentViewFadeIn() = testScope.runTest { val values by collectValues(underTest.deviceEntryParentViewAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index 90b83620084c..30b87bbbcf11 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.testKosmos import com.android.systemui.util.mockito.whenever import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -65,6 +66,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun bouncerAlpha() = testScope.runTest { val values by collectValues(underTest.bouncerAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( listOf( @@ -83,6 +85,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun bouncerAlpha_runDimissFromKeyguard() = testScope.runTest { val values by collectValues(underTest.bouncerAlpha) + runCurrent() whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true) @@ -95,7 +98,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { testScope, ) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(0f) } } @@ -103,11 +106,12 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun lockscreenAlpha() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) keyguardTransitionRepository.sendTransitionStep(step(1f)) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(0f) } } @@ -115,13 +119,14 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun lockscreenAlpha_runDimissFromKeyguard() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) keyguardTransitionRepository.sendTransitionStep(step(1f)) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(1f) } } @@ -129,13 +134,14 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { fun lockscreenAlpha_leaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.lockscreenAlpha) + runCurrent() sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) keyguardTransitionRepository.sendTransitionStep(step(1f)) - assertThat(values.size).isEqualTo(1) + assertThat(values.size).isEqualTo(2) values.forEach { assertThat(it).isEqualTo(1f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java index 01fe40f6dff6..d0e05fa60114 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java @@ -42,19 +42,15 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; -import android.os.Handler; -import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import android.util.SparseArray; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; @@ -86,8 +82,7 @@ import java.util.Collection; import java.util.concurrent.Executor; @SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@RunWith(AndroidJUnit4.class) public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase { @Mock private NotificationPresenter mPresenter; @@ -128,6 +123,7 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa private NotificationEntry mSecondaryUserNotif; private NotificationEntry mWorkProfileNotif; private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic(); + private Executor mMainExecutor = Runnable::run; // Direct executor private Executor mBackgroundExecutor = Runnable::run; // Direct executor @Before @@ -156,8 +152,6 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa mSecondaryUser)); when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn( Lists.newArrayList(mSecondaryUser, mCommunalUser)); - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, - Handler.createAsync(Looper.myLooper())); Notification notifWithPrivateVisibility = new Notification(); notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE; @@ -378,28 +372,24 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa // first call explicitly sets user 0 to not public; notifies mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener).onNotificationStateChanged(); clearInvocations(listener); // calling again has no changes; does not notify mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener, never()).onNotificationStateChanged(); // Calling again with keyguard now showing makes user 0 public; notifies when(mKeyguardStateController.isShowing()).thenReturn(true); mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener).onNotificationStateChanged(); clearInvocations(listener); // calling again has no changes; does not notify mLockscreenUserManager.updatePublicMode(); - TestableLooper.get(this).processAllMessages(); assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); verify(listener, never()).onNotificationStateChanged(); } @@ -595,8 +585,7 @@ public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCa (() -> mOverviewProxyService), NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager, mStatusBarStateController, - Handler.createAsync(Looper.myLooper()), - Handler.createAsync(Looper.myLooper()), + mMainExecutor, mBackgroundExecutor, mDeviceProvisionedController, mKeyguardStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 757f16cac227..bcc0710359cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -29,10 +29,11 @@ import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.os.UserHandle.USER_ALL; import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; -import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -62,13 +63,11 @@ import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.provider.Settings; -import android.testing.TestableLooper; import android.util.SparseArray; import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; @@ -88,6 +87,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.time.FakeSystemClock; + import com.google.android.collect.Lists; import org.junit.After; @@ -109,7 +109,6 @@ import platform.test.runner.parameterized.Parameters; @SmallTest @RunWith(ParameterizedAndroidJunit4.class) -@TestableLooper.RunWithLooper public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Parameters(name = "{0}") @@ -197,8 +196,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mSecondaryUser)); when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn( Lists.newArrayList(mSecondaryUser, mCommunalUser)); - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, - mockExecutorHandler(mMainExecutor)); Notification notifWithPrivateVisibility = new Notification(); notifWithPrivateVisibility.visibility = VISIBILITY_PRIVATE; @@ -949,8 +946,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { (() -> mOverviewProxyService), NotificationLockscreenUserManagerTest.this.mKeyguardManager, mStatusBarStateController, - mockExecutorHandler(mMainExecutor), - mockExecutorHandler(mBackgroundExecutor), + mMainExecutor, mBackgroundExecutor, mDeviceProvisionedController, mKeyguardStateController, diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java index 3e5e8a0d462e..f0ce4604309d 100644 --- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java @@ -18,6 +18,8 @@ package com.android.systemui.plugins; import android.content.ComponentName; +import java.util.function.BiConsumer; + /** * Provides the ability for consumers to control plugin lifecycle. * @@ -33,11 +35,8 @@ public interface PluginLifecycleManager<T extends Plugin> { /** Returns the currently loaded plugin instance (if plugin is loaded) */ T getPlugin(); - /** Returns true if the lifecycle manager should log debug messages */ - boolean getIsDebug(); - - /** Sets whether or not hte lifecycle manager should log debug messages */ - void setIsDebug(boolean debug); + /** Log tag and messages will be sent to the provided Consumer */ + void setLogFunc(BiConsumer<String, String> logConsumer); /** returns true if the plugin is currently loaded */ default boolean isLoaded() { diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml new file mode 100644 index 000000000000..2161a62ada9c --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 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. +*/ +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="17dp" + android:height="17dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,10h-2v8h2V10z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,20h-2v2h2V20z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml new file mode 100644 index 000000000000..2161a62ada9c --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 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. +*/ +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="17dp" + android:height="17dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,10h-2v8h2V10z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,20h-2v2h2V20z"/> +</vector> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 6d7ce0623817..e602d6c8848d 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -107,4 +107,13 @@ <include layout="@layout/ambient_indication" android:id="@id/ambient_indication_container" /> + + <FrameLayout + android:id="@+id/smartspace_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_marginBottom="@dimen/ambient_indication_margin_bottom" + android:visibility="gone"> + </FrameLayout> </com.android.systemui.statusbar.phone.KeyguardBottomAreaView> diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml index fc0bf242dc1e..4cb7591a3502 100644 --- a/packages/SystemUI/res/layout/keyguard_status_bar.xml +++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml @@ -23,7 +23,6 @@ android:layout_width="match_parent" android:layout_height="@dimen/status_bar_header_height_keyguard" android:baselineAligned="false" - android:gravity="center_vertical" > <LinearLayout diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 5e109053c994..f55a11e2f3f0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -449,11 +449,11 @@ <!-- Content description after successful auth when confirmation required --> <string name="fingerprint_dialog_authenticated_confirmation">Press the unlock icon to continue</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> - <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string> + <string name="fingerprint_dialog_use_fingerprint_instead">Face not recognized. Use fingerprint instead.</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> <string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string> <!-- Message shown to inform the user a face cannot be recognized. [CHAR LIMIT=25] --> - <string name="keyguard_face_failed">Can\u2019t recognize face</string> + <string name="keyguard_face_failed">Face not recognized</string> <!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] --> <string name="keyguard_suggest_fingerprint">Use fingerprint instead</string> <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=59] --> @@ -2284,6 +2284,8 @@ <string name="notification_channel_storage">Storage</string> <!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] --> <string name="notification_channel_hints">Hints</string> + <!-- Title for the notification channel for accessibility related (i.e. accessibility floating menu). [CHAR LIMIT=NONE] --> + <string name="notification_channel_accessibility">Accessibility</string> <!-- App label of the instant apps notification [CHAR LIMIT=60] --> <string name="instant_apps">Instant Apps</string> @@ -2554,6 +2556,11 @@ <string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string> <!-- Text for the undo action button of the message view of the accessibility floating menu to perform undo operation. [CHAR LIMIT=30]--> <string name="accessibility_floating_button_undo">Undo</string> + <!-- Notification title shown when accessibility floating button is in hidden state. [CHAR LIMIT=NONE] --> + <string name="accessibility_floating_button_hidden_notification_title">Accessibility button hidden</string> + <!-- Notification content text to explain user can tap notification to bring back accessibility floating button. [CHAR LIMIT=NONE] --> + <string name="accessibility_floating_button_hidden_notification_text">Tap to show accessibility button</string> + <!-- Text for the message view with undo action of the accessibility floating menu to show which feature shortcut was removed. [CHAR LIMIT=30]--> <string name="accessibility_floating_button_undo_message_label_text"><xliff:g id="feature name" example="Magnification">%s</xliff:g> shortcut removed</string> <!-- Text for the message view with undo action of the accessibility floating menu to show how many features shortcuts were removed. [CHAR LIMIT=30]--> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 05106c904d3d..326c7ef52fce 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -34,9 +34,6 @@ java_library { srcs: [ ":statslog-SystemUI-java-gen", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } android_library { @@ -74,9 +71,6 @@ android_library { min_sdk_version: "current", plugins: ["dagger2-compiler"], kotlincflags: ["-Xjvm-default=all"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -88,9 +82,6 @@ java_library { static_kotlin_stdlib: false, java_version: "1.8", min_sdk_version: "current", - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -110,7 +101,4 @@ java_library { }, java_version: "1.8", min_sdk_version: "current", - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SystemUI/shared/lint-baseline.xml b/packages/SystemUI/shared/lint-baseline.xml deleted file mode 100644 index 4bd6729227e8..000000000000 --- a/packages/SystemUI/shared/lint-baseline.xml +++ /dev/null @@ -1,708 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.os.RemoteException#rethrowFromSystemServer`" - errorLine1=" throw e.rethrowFromSystemServer();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java" - line="90" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`" - errorLine1=" mBuffer != null ? mBuffer.getHardwareBuffer() : null, mRect);" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecCompat.java" - line="39" - column="43"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`" - errorLine1=" ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="57" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`" - errorLine1=" : new ParcelableColorSpace(bitmap.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="58" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`" - errorLine1=" bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="61" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Cast from `ParcelableColorSpace` to `Parcelable` requires API level 31 (current min is 26)" - errorLine1=" bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);" - errorLine2=" ~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="62" - column="47"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`" - errorLine1=" return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer)," - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="84" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.ParcelableColorSpace#getColorSpace`" - errorLine1=" colorSpace.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="85" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java" - line="122" - column="47"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`" - errorLine1=" mPluginActions = new ArraySet<>(mSharedPrefs.getStringSet(PLUGIN_ACTIONS, null));" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java" - line="41" - column="26"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`" - errorLine1=" return new ArraySet<>(mPluginActions);" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java" - line="45" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 28 (current min is 26): `android.graphics.Bitmap#createBitmap`" - errorLine1=" return Bitmap.createBitmap(picture);" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java" - line="113" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Builder`" - errorLine1=" mSurface = new SurfaceControl.Builder()" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="116" - column="24"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setName`" - errorLine1=" .setName("Transition Unrotate")" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="117" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setParent`" - errorLine1=" .setParent(parent)" - errorLine2=" ~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="119" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#build`" - errorLine1=" .build();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="120" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`" - errorLine1=" t.reparent(child, mSurface);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="137" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" SurfaceControl.Transaction t = new SurfaceControl.Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="143" - column="44"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`" - errorLine1=" t.reparent(mRotateChildren.get(i), rootLeash);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="145" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="148" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterLauncher.mSurface, launcherLayer);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="200" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="206" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(leash, info.getChanges().size() * 3 - i);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="216" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="223" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterWallpaper.mSurface, -1);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="233" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="238" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`" - errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="101" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="192" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#isRunning`" - errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="116" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#isRunning`" - errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="210" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`" - errorLine1=" leash.mSurfaceControl.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="159" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`" - errorLine1=" mStartLeash.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="161" - column="25"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(change.getLeash(), info.getChanges().size() * 3 - i);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="97" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="105" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="107" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`" - errorLine1=" return mSurfaceControl != null && mSurfaceControl.isValid();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java" - line="41" - column="59"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#release`" - errorLine1=" mSurfaceControlViewHost.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java" - line="61" - column="37"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.view.SurfaceView#getHostToken`" - errorLine1=" bundle.putBinder(KEY_HOST_TOKEN, surfaceView.getHostToken());" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="34" - column="54"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceView#getSurfaceControl`" - errorLine1=" bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="35" - column="63"/> - </issue> - - <issue - id="NewApi" - message="Cast from `SurfaceControl` to `Parcelable` requires API level 29 (current min is 26)" - errorLine1=" bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="35" - column="51"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`" - errorLine1=" if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="107" - column="79"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" Transaction t = new Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="113" - column="33"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="122" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(surface, alpha);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="361" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(surface, layer);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="364" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`" - errorLine1=" ComponentName sourceComponent = t.origActivity != null" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="73" - column="45"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`" - errorLine1=" ? t.origActivity" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="75" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" this.id = t.taskId;" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="78" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#baseIntent`" - errorLine1=" this.baseIntent = t.baseIntent;" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="80" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`" - errorLine1=" ActivityManager.TaskDescription td = taskInfo.taskDescription;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="242" - column="46"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`" - errorLine1=" taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="246" - column="72"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`" - errorLine1=" return info.topActivity;" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java" - line="49" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`" - errorLine1=" return info.taskDescription;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java" - line="53" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`" - errorLine1=" onTaskMovedToFront(taskInfo.taskId);" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java" - line="70" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" onTaskMovedToFront(taskInfo.taskId);" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java" - line="70" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`" - errorLine1=" thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java" - line="69" - column="36"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.app.WallpaperColors#getColorHints`" - errorLine1=" (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TonalCompat.java" - line="42" - column="29"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" mTransaction = new Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="31" - column="24"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" mTransaction.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="35" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setBufferSize`" - errorLine1=" mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="54" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" mTransaction.setLayer(surfaceControl.mSurfaceControl, z);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="59" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="64" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java" - line="64" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`" - errorLine1=" .getFloat(Resources.getSystem().getIdentifier(" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java" - line="46" - column="18"/> - </issue> - -</issues> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java index 387f2e1aa430..87cc86f18fdc 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java @@ -38,6 +38,7 @@ import dalvik.system.PathClassLoader; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.function.BiConsumer; import java.util.function.Supplier; /** @@ -57,7 +58,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager private final PluginFactory<T> mPluginFactory; private final String mTag; - private boolean mIsDebug = false; + private BiConsumer<String, String> mLogConsumer = null; private Context mPluginContext; private T mPlugin; @@ -86,17 +87,13 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return mTag; } - public boolean getIsDebug() { - return mIsDebug; + public void setLogFunc(BiConsumer logConsumer) { + mLogConsumer = logConsumer; } - public void setIsDebug(boolean debug) { - mIsDebug = debug; - } - - private void logDebug(String message) { - if (mIsDebug) { - Log.i(mTag, message); + private void log(String message) { + if (mLogConsumer != null) { + mLogConsumer.accept(mTag, message); } } @@ -105,19 +102,19 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager boolean loadPlugin = mListener.onPluginAttached(this); if (!loadPlugin) { if (mPlugin != null) { - logDebug("onCreate: auto-unload"); + log("onCreate: auto-unload"); unloadPlugin(); } return; } if (mPlugin == null) { - logDebug("onCreate auto-load"); + log("onCreate auto-load"); loadPlugin(); return; } - logDebug("onCreate: load callbacks"); + log("onCreate: load callbacks"); mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments @@ -129,7 +126,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager /** Alerts listener and plugin that the plugin is being shutdown. */ public synchronized void onDestroy() { - logDebug("onDestroy"); + log("onDestroy"); unloadPlugin(); mListener.onPluginDetached(this); } @@ -145,7 +142,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager */ public synchronized void loadPlugin() { if (mPlugin != null) { - logDebug("Load request when already loaded"); + log("Load request when already loaded"); return; } @@ -157,7 +154,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return; } - logDebug("Loaded plugin; running callbacks"); + log("Loaded plugin; running callbacks"); mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments @@ -174,11 +171,11 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager */ public synchronized void unloadPlugin() { if (mPlugin == null) { - logDebug("Unload request when already unloaded"); + log("Unload request when already unloaded"); return; } - logDebug("Unloading plugin, running callbacks"); + log("Unloading plugin, running callbacks"); mListener.onPluginUnloaded(mPlugin, this); if (!(mPlugin instanceof PluginFragment)) { // Only call onDestroy for plugins that aren't fragments, as fragments diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl index 823bd2d147d3..7088829bd2a3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl @@ -88,18 +88,23 @@ oneway interface IOverviewProxy { */ void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22; - /** - * Sent when split keyboard shortcut is triggered to enter stage split. - */ - void enterStageSplitFromRunningApp(boolean leftOrTop) = 25; - - /** - * Sent when the surface for navigation bar is created or changed - */ - void onNavigationBarSurface(in SurfaceControl surface) = 26; - - /** - * Sent when the task bar stash state is toggled. - */ - void onTaskbarToggled() = 27; + /** + * Sent when when navigation bar luma sampling is enabled or disabled. + */ + void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) = 23; + + /** + * Sent when split keyboard shortcut is triggered to enter stage split. + */ + void enterStageSplitFromRunningApp(boolean leftOrTop) = 25; + + /** + * Sent when the surface for navigation bar is created or changed + */ + void onNavigationBarSurface(in SurfaceControl surface) = 26; + + /** + * Sent when the task bar stash state is toggled. + */ + void onTaskbarToggled() = 27; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 74b975cb7232..de7c12db6b11 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -22,6 +22,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.Flags.migrateClocksToBlueprint; +import static com.android.systemui.Flags.smartspaceRelocateToBottom; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -421,6 +422,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS return; } + if (smartspaceRelocateToBottom()) { + return; + } + mSmartspaceView = mSmartspaceController.buildAndConnectView(mView); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( MATCH_PARENT, WRAP_CONTENT); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index e457ca18d071..8e5d0dac7bef 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -1111,7 +1111,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } private boolean canDisplayUserSwitcher() { - return mFeatureFlags.isEnabled(Flags.BOUNCER_USER_SWITCHER); + return getContext().getResources().getBoolean(R.bool.config_enableBouncerUserSwitcher); } private void configureMode() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index c5e70703cd2b..5729119a582a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -105,6 +105,13 @@ public class KeyguardSimPinViewController @Override protected void onViewAttached() { super.onViewAttached(); + mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); } @Override @@ -128,14 +135,12 @@ public class KeyguardSimPinViewController @Override public void onResume(int reason) { super.onResume(reason); - mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mView.resetState(); } @Override public void onPause() { super.onPause(); - mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); // dismiss the dialog. if (mSimUnlockProgressDialog != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 2a54a4eee657..d372f5a616b1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -20,6 +20,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.END; import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION; +import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.animation.Animator; @@ -492,7 +493,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV boolean splitShadeEnabled, boolean shouldBeCentered, boolean animate) { - mKeyguardClockSwitchController.setSplitShadeCentered(splitShadeEnabled && shouldBeCentered); + if (migrateClocksToBlueprint()) { + mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered); + } else { + mKeyguardClockSwitchController.setSplitShadeCentered( + splitShadeEnabled && shouldBeCentered); + } if (mStatusViewCentered == shouldBeCentered) { return; } @@ -548,8 +554,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV ClockController clock = mKeyguardClockSwitchController.getClock(); boolean customClockAnimation = clock != null && clock.getLargeClock().getConfig().getHasCustomPositionUpdatedAnimation(); - - if (customClockAnimation) { + // When migrateClocksToBlueprint is on, customized clock animation is conducted in + // KeyguardClockViewBinder + if (customClockAnimation && !migrateClocksToBlueprint()) { // Find the clock, so we can exclude it from this transition. FrameLayout clockContainerView = mView.findViewById(R.id.lockscreen_clock_view_large); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java index 49e0df6f6afc..568b24dbd4f3 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java @@ -24,6 +24,7 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.dynamicanimation.animation.DynamicAnimation; +import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.common.bubbles.DismissView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -116,6 +117,11 @@ class DragToInteractAnimationController { mMagnetizedObject.setMagnetListener(magnetListener); } + @VisibleForTesting + MagnetizedObject.MagnetListener getMagnetListener() { + return mMagnetizedObject.getMagnetListener(); + } + void maybeConsumeDownMotionEvent(MotionEvent event) { mMagnetizedObject.maybeConsumeMotionEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java new file mode 100644 index 000000000000..b5eeaa18447e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java @@ -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.accessibility.floatingmenu; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.UserHandle; + +import com.android.systemui.res.R; +import com.android.systemui.util.NotificationChannels; + +class MenuNotificationFactory { + public static final String ACTION_UNDO = + "com.android.systemui.accessibility.floatingmenu.action.UNDO"; + public static final String ACTION_DELETE = + "com.android.systemui.accessibility.floatingmenu.action.DELETE"; + + private final Context mContext; + + MenuNotificationFactory(Context context) { + mContext = context; + } + + public Notification createHiddenNotification() { + final CharSequence title = mContext.getText( + R.string.accessibility_floating_button_hidden_notification_title); + final CharSequence content = mContext.getText( + R.string.accessibility_floating_button_hidden_notification_text); + + return new Notification.Builder(mContext, NotificationChannels.ALERTS) + .setContentTitle(title) + .setContentText(content) + .setSmallIcon(R.drawable.ic_settings_24dp) + .setContentIntent(buildUndoIntent()) + .setDeleteIntent(buildDeleteIntent()) + .setColor(mContext.getResources().getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_SYSTEM) + .build(); + } + + private PendingIntent buildUndoIntent() { + final Intent intent = new Intent(ACTION_UNDO); + + return PendingIntent.getBroadcast(mContext, /* requestCode= */ 0, intent, + PendingIntent.FLAG_IMMUTABLE); + + } + + private PendingIntent buildDeleteIntent() { + final Intent intent = new Intent(ACTION_DELETE); + + return PendingIntent.getBroadcastAsUser(mContext, /* requestCode= */ 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT); + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 62d5feb7d024..6869bbaedcf3 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -26,16 +26,21 @@ import static com.android.internal.accessibility.common.ShortcutConstants.Access import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType; import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState; import static com.android.systemui.accessibility.floatingmenu.MenuMessageView.Index; +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE; +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO; import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.IntDef; import android.annotation.StringDef; import android.annotation.SuppressLint; +import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.ComponentCallbacks; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; @@ -58,6 +63,7 @@ import androidx.lifecycle.Observer; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.util.Preconditions; import com.android.systemui.Flags; import com.android.systemui.res.R; @@ -91,6 +97,8 @@ class MenuViewLayer extends FrameLayout implements private final MenuViewAppearance mMenuViewAppearance; private final MenuAnimationController mMenuAnimationController; private final AccessibilityManager mAccessibilityManager; + private final NotificationManager mNotificationManager; + private final MenuNotificationFactory mNotificationFactory; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final IAccessibilityFloatingMenu mFloatingMenu; private final SecureSettings mSecureSettings; @@ -103,7 +111,9 @@ class MenuViewLayer extends FrameLayout implements private final Rect mImeInsetsRect = new Rect(); private boolean mIsMigrationTooltipShowing; private boolean mShouldShowDockTooltip; + private boolean mIsNotificationShown; private Optional<MenuEduTooltipView> mEduTooltipView = Optional.empty(); + private BroadcastReceiver mNotificationActionReceiver; @IntDef({ LayerIndex.MENU_VIEW, @@ -184,10 +194,16 @@ class MenuViewLayer extends FrameLayout implements mMenuViewAppearance = new MenuViewAppearance(context, windowManager); mMenuView = new MenuView(context, mMenuViewModel, mMenuViewAppearance); mMenuAnimationController = mMenuView.getMenuAnimationController(); - mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage); + if (Flags.floatingMenuDragToHide()) { + mMenuAnimationController.setDismissCallback(this::hideMenuAndShowNotification); + } else { + mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage); + } mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction); mDismissView = new DismissView(context); DismissViewUtils.setup(mDismissView); + mNotificationFactory = new MenuNotificationFactory(context); + mNotificationManager = context.getSystemService(NotificationManager.class); mDragToInteractAnimationController = new DragToInteractAnimationController( mDismissView, mMenuView); mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() { @@ -204,7 +220,11 @@ class MenuViewLayer extends FrameLayout implements @Override public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { - hideMenuAndShowMessage(); + if (Flags.floatingMenuDragToHide()) { + hideMenuAndShowNotification(); + } else { + hideMenuAndShowMessage(); + } mDismissView.hide(); mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false); } @@ -218,18 +238,25 @@ class MenuViewLayer extends FrameLayout implements mMessageView = new MenuMessageView(context); mMenuView.setOnTargetFeaturesChangeListener(newTargetFeatures -> { - if (newTargetFeatures.size() < 1) { - return; - } - - // During the undo action period, the pending action will be canceled and undo back - // to the previous state if users did any action related to the accessibility features. - if (mMessageView.getVisibility() == VISIBLE) { + if (Flags.floatingMenuDragToHide()) { + dismissNotification(); undo(); - } + } else { + if (newTargetFeatures.size() < 1) { + return; + } + + // During the undo action period, the pending action will be canceled and undo back + // to the previous state if users did any action related to the accessibility + // features. + if (mMessageView.getVisibility() == VISIBLE) { + undo(); + } - final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW); - messageText.setText(getMessageText(newTargetFeatures)); + + final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW); + messageText.setText(getMessageText(newTargetFeatures)); + } }); addView(mMenuView, LayerIndex.MENU_VIEW); @@ -456,6 +483,50 @@ class MenuViewLayer extends FrameLayout implements mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE)); } + private void hideMenuAndShowNotification() { + mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE)); + showNotification(); + } + + private void showNotification() { + registerReceiverIfNeeded(); + if (!mIsNotificationShown) { + mNotificationManager.notify( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN, + mNotificationFactory.createHiddenNotification()); + mIsNotificationShown = true; + } + } + + private void dismissNotification() { + unregisterReceiverIfNeeded(); + if (mIsNotificationShown) { + mNotificationManager.cancel( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN); + mIsNotificationShown = false; + } + } + + private void registerReceiverIfNeeded() { + if (mNotificationActionReceiver != null) { + return; + } + mNotificationActionReceiver = new MenuNotificationActionReceiver(); + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_UNDO); + intentFilter.addAction(ACTION_DELETE); + getContext().registerReceiver(mNotificationActionReceiver, intentFilter, + Context.RECEIVER_EXPORTED); + } + + private void unregisterReceiverIfNeeded() { + if (mNotificationActionReceiver == null) { + return; + } + getContext().unregisterReceiver(mNotificationActionReceiver); + mNotificationActionReceiver = null; + } + private void undo() { mHandler.removeCallbacksAndMessages(/* token= */ null); mMessageView.setVisibility(GONE); @@ -464,4 +535,23 @@ class MenuViewLayer extends FrameLayout implements mMenuView.setVisibility(VISIBLE); mMenuAnimationController.startGrowAnimation(); } + + @VisibleForTesting + DragToInteractAnimationController getDragToInteractAnimationController() { + return mDragToInteractAnimationController; + } + + private class MenuNotificationActionReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (ACTION_UNDO.equals(action)) { + dismissNotification(); + undo(); + } else if (ACTION_DELETE.equals(action)) { + dismissNotification(); + mDismissMenuAction.run(); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt new file mode 100644 index 000000000000..889023e8dab6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2024 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.communal.log + +import com.android.internal.logging.UiEventLogger +import com.android.systemui.CoreStartable +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.shared.log.CommunalUiEvent +import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.kotlin.pairwise +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach + +/** A [CoreStartable] responsible for logging metrics for the communal hub. */ +@SysUISingleton +class CommunalLoggerStartable +@Inject +constructor( + @Background private val backgroundScope: CoroutineScope, + private val communalInteractor: CommunalInteractor, + private val uiEventLogger: UiEventLogger, +) : CoreStartable { + + override fun start() { + communalInteractor.transitionState + .map { state -> + when { + state.isOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_SHOWN + state.isNotOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_GONE + else -> null + } + } + .filterNotNull() + .distinctUntilChanged() + // Drop the default value. + .drop(1) + .onEach { uiEvent -> uiEventLogger.log(uiEvent) } + .launchIn(backgroundScope) + + communalInteractor.transitionState + .pairwise() + .map { (old, new) -> + when { + new.isOnCommunal() && old.isSwipingToCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH + new.isOnCommunal() && old.isSwipingFromCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL + new.isNotOnCommunal() && old.isSwipingFromCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH + new.isNotOnCommunal() && old.isSwipingToCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL + new.isSwipingToCommunal() && old.isNotOnCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START + new.isSwipingFromCommunal() && old.isOnCommunal() -> + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START + else -> null + } + } + .filterNotNull() + .distinctUntilChanged() + .onEach { uiEvent -> uiEventLogger.log(uiEvent) } + .launchIn(backgroundScope) + } +} + +/** Whether currently in communal scene. */ +private fun ObservableCommunalTransitionState.isOnCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Idle && scene == CommunalSceneKey.Communal +} + +/** Whether currently in a scene other than communal. */ +private fun ObservableCommunalTransitionState.isNotOnCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Idle && scene != CommunalSceneKey.Communal +} + +/** Whether currently transitioning from another scene to communal. */ +private fun ObservableCommunalTransitionState.isSwipingToCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Transition && + toScene == CommunalSceneKey.Communal && + isInitiatedByUserInput +} + +/** Whether currently transitioning from communal to another scene. */ +private fun ObservableCommunalTransitionState.isSwipingFromCommunal(): Boolean { + return this is ObservableCommunalTransitionState.Transition && + fromScene == CommunalSceneKey.Communal && + isInitiatedByUserInput +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt index e167f3e263fe..b64c1955b43d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt @@ -22,8 +22,6 @@ import com.android.internal.logging.UiEventLogger.UiEventEnum /** UI events for the Communal Hub. */ enum class CommunalUiEvent(private val id: Int) : UiEventEnum { @UiEvent(doc = "Communal Hub is fully shown") COMMUNAL_HUB_SHOWN(1566), - @UiEvent(doc = "Communal Hub starts entering") COMMUNAL_HUB_ENTERING(1575), - @UiEvent(doc = "Communal Hub starts exiting") COMMUNAL_HUB_EXITING(1576), @UiEvent(doc = "Communal Hub is fully gone") COMMUNAL_HUB_GONE(1577), @UiEvent(doc = "Communal Hub times out") COMMUNAL_HUB_TIMEOUT(1578), @UiEvent(doc = "The visible content in the Communal Hub is fully loaded and rendered") diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 28f48ce1e647..84708a49f469 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -17,17 +17,12 @@ package com.android.systemui.communal.ui.viewmodel import android.content.ComponentName -import android.os.PowerManager -import android.os.SystemClock -import android.view.MotionEvent import android.widget.RemoteViews import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController -import javax.inject.Provider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf @@ -35,8 +30,6 @@ import kotlinx.coroutines.flow.flowOf /** The base view model for the communal hub. */ abstract class BaseCommunalViewModel( private val communalInteractor: CommunalInteractor, - private val shadeViewController: Provider<ShadeViewController>, - private val powerManager: PowerManager, val mediaHost: MediaHost, ) { val isKeyguardVisible: Flow<Boolean> = communalInteractor.isKeyguardVisible @@ -71,26 +64,6 @@ abstract class BaseCommunalViewModel( return true } - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block - // touches anymore. - /** Called when a touch is received outside the edge swipe area when hub mode is closed. */ - fun onOuterTouch(motionEvent: MotionEvent) { - // Forward the touch to the shade so that basic gestures like swipe up/down for - // shade/bouncer work. - shadeViewController.get().handleExternalTouch(motionEvent) - } - - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block - // touches anymore. - /** Called to refresh the screen timeout when a user touch is received. */ - fun onUserActivity() { - powerManager.userActivity( - SystemClock.uptimeMillis(), - PowerManager.USER_ACTIVITY_EVENT_TOUCH, - 0 - ) - } - /** A list of all the communal content to be displayed in the communal hub. */ abstract val communalContent: Flow<List<CommunalContentModel>> diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 0cbf3f1312e2..7faf653cc177 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -23,7 +23,6 @@ import android.app.ActivityOptions import android.appwidget.AppWidgetHost import android.content.ActivityNotFoundException import android.content.ComponentName -import android.os.PowerManager import android.widget.RemoteViews import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.domain.interactor.CommunalInteractor @@ -32,11 +31,9 @@ import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule -import com.android.systemui.shade.ShadeViewController import com.android.systemui.util.nullableAtomicReference import javax.inject.Inject import javax.inject.Named -import javax.inject.Provider import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -49,11 +46,9 @@ class CommunalEditModeViewModel constructor( private val communalInteractor: CommunalInteractor, private val appWidgetHost: AppWidgetHost, - shadeViewController: Provider<ShadeViewController>, - powerManager: PowerManager, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, private val uiEventLogger: UiEventLogger, -) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) { +) : BaseCommunalViewModel(communalInteractor, mediaHost) { private companion object { private const val KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle" 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 1c696851bb70..7a96fabd2433 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 @@ -16,7 +16,6 @@ package com.android.systemui.communal.ui.viewmodel -import android.os.PowerManager import android.widget.RemoteViews import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor @@ -26,10 +25,8 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule -import com.android.systemui.shade.ShadeViewController import javax.inject.Inject import javax.inject.Named -import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job @@ -51,10 +48,8 @@ constructor( private val communalInteractor: CommunalInteractor, private val interactionHandler: WidgetInteractionHandler, tutorialInteractor: CommunalTutorialInteractor, - shadeViewController: Provider<ShadeViewController>, - powerManager: PowerManager, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, -) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) { +) : BaseCommunalViewModel(communalInteractor, mediaHost) { @OptIn(ExperimentalCoroutinesApi::class) override val communalContent: Flow<List<CommunalContentModel>> = tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode -> diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index bfc6f2b14acd..380ed61a556d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -30,6 +30,8 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import com.android.internal.logging.UiEventLogger +import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent import javax.inject.Inject @@ -41,6 +43,7 @@ class EditWidgetsActivity constructor( private val communalViewModel: CommunalEditModeViewModel, private var windowManagerService: IWindowManager? = null, + private val uiEventLogger: UiEventLogger, ) : ComponentActivity() { companion object { private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag" @@ -54,6 +57,8 @@ constructor( registerForActivityResult(StartActivityForResult()) { result -> when (result.resultCode) { RESULT_OK -> { + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_WIDGET_PICKER_SHOWN) + result.data?.let { intent -> val isPendingWidgetDrag = intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false) @@ -144,4 +149,16 @@ constructor( communalViewModel.setConfigurationResult(resultCode) } } + + override fun onStart() { + super.onStart() + + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN) + } + + override fun onStop() { + super.onStop() + + uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_GONE) + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 87a736d926b5..8d82b552fc1e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -24,6 +24,7 @@ import com.android.systemui.accessibility.Magnification import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.BiometricNotificationService import com.android.systemui.clipboardoverlay.ClipboardListener +import com.android.systemui.communal.log.CommunalLoggerStartable import com.android.systemui.controls.dagger.StartControlsStartableModule import com.android.systemui.dagger.qualifiers.PerUser import com.android.systemui.dreams.AssistantAttentionMonitor @@ -318,4 +319,9 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(KeyguardDismissBinder::class) abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable + + @Binds + @IntoMap + @ClassKey(CommunalLoggerStartable::class) + abstract fun bindCommunalLoggerStartable(impl: CommunalLoggerStartable): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java index 4e4b79c5cfa2..7f3b5eba96c6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java @@ -155,12 +155,12 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { return true; } - // Don't set expansion if the user doesn't have a pin/password set so that no - // animations are played we're not transitioning to the bouncer. - if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) { - // Return false so the gesture is not consumed, allowing the dream to wake - // if it wants instead of doing nothing. - return false; + // If scrolling up and keyguard is not locked, dismiss the dream since there's + // no bouncer to show. + if (e1.getY() > e2.getY() + && !mLockPatternUtils.isSecure(mUserTracker.getUserId())) { + mCentralSurfaces.get().awakenDreams(); + return true; } // For consistency, we adopt the expansion definition found in the diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 699532cbfca3..89a983ba5d90 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -122,11 +122,6 @@ object Flags { val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag("new_unlock_swipe_animation") val CHARGING_RIPPLE = resourceBooleanFlag(R.bool.flag_charging_ripple, "charging_ripple") - // TODO(b/254512281): Tracking Bug - @JvmField - val BOUNCER_USER_SWITCHER = - resourceBooleanFlag(R.bool.config_enableBouncerUserSwitcher, "bouncer_user_switcher") - // TODO(b/254512676): Tracking Bug @JvmField val LOCKSCREEN_CUSTOM_CLOCKS = @@ -377,9 +372,6 @@ object Flags { // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging") - // TODO(b/254512758): Tracking Bug - @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag("rounded_box_ripple") - // TODO(b/273509374): Tracking Bug @JvmField val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index a34730e2d71f..5cebd96249b6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1486,6 +1486,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mOrderUnlockAndWake = context.getResources().getBoolean( com.android.internal.R.bool.config_orderUnlockAndWake); + + mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); + mShowKeyguardWakeLock.setReferenceCounted(false); } public void userActivity() { @@ -1493,9 +1496,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void setupLocked() { - mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); - mShowKeyguardWakeLock.setReferenceCounted(false); - IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SHUTDOWN); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt index cb0f18630324..42f14f1eb317 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt @@ -140,8 +140,9 @@ constructor( override val revealAmount: Flow<Float> = callbackFlow { val updateListener = Animator.AnimatorUpdateListener { - val value = (it as ValueAnimator).animatedValue - trySend(value as Float) + val value = (it as ValueAnimator).animatedValue as Float + trySend(value) + if (value <= 0.0f || value >= 1.0f) { scrimLogger.d(TAG, "revealAmount", value) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index f7d1543e4650..5b0c562bfdb8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -154,6 +154,8 @@ constructor( val dozingToLockscreenTransition: Flow<TransitionStep> = repository.transition(DOZING, LOCKSCREEN) + val transitions = repository.transitions + /** Receive all [TransitionStep] matching a filter of [from]->[to] */ fun transition(from: KeyguardState, to: KeyguardState): Flow<TransitionStep> { return repository.transition(from, to) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt index fbf693625f63..19d00cfea114 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt @@ -22,12 +22,15 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @ExperimentalCoroutinesApi @@ -39,6 +42,7 @@ constructor( private val lightRevealScrimRepository: LightRevealScrimRepository, @Application private val scope: CoroutineScope, private val scrimLogger: ScrimLogger, + powerInteractor: PowerInteractor, ) { init { @@ -73,7 +77,16 @@ constructor( lightRevealScrimRepository.revealEffect ) - val revealAmount = lightRevealScrimRepository.revealAmount + val revealAmount = + lightRevealScrimRepository.revealAmount.filter { + // When the screen is off we do not want to keep producing frames as this is causing + // (invisible) jank. However, we need to still pass through 1f and 0f to ensure that the + // correct end states are respected even if the screen turned off (or was still off) + // when the animation finished + powerInteractor.screenPowerState.value != ScreenPowerState.SCREEN_OFF || + it == 1f || + it == 0f + } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt index cf1d2477c9af..4abda741d495 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt @@ -17,9 +17,13 @@ package com.android.systemui.keyguard.ui import android.view.animation.Interpolator import com.android.app.animation.Interpolators.LINEAR +import com.android.app.tracing.coroutines.launch import com.android.keyguard.logging.KeyguardTransitionAnimationLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING @@ -31,10 +35,12 @@ import kotlin.math.min import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull /** * Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and @@ -45,21 +51,49 @@ class KeyguardTransitionAnimationFlow @Inject constructor( @Application private val scope: CoroutineScope, + private val transitionInteractor: KeyguardTransitionInteractor, private val logger: KeyguardTransitionAnimationLogger, ) { + private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>() - /** - * Invoke once per transition between FROM->TO states to get access to - * [SharedFlowBuilder#sharedFlow]. - */ + init { + scope.launch("KeyguardTransitionAnimationFlow") { + transitionInteractor.transitions.collect { + // FROM->TO + transitionMap[Edge(it.from, it.to)]?.emit(it) + // FROM->(ANY) + transitionMap[Edge(it.from, null)]?.emit(it) + // (ANY)->TO + transitionMap[Edge(null, it.to)]?.emit(it) + } + } + } + + private fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> { + return transitionMap.getOrPut(edge) { + MutableSharedFlow<TransitionStep>( + extraBufferCapacity = 10, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + } + } + + /** Invoke once per transition between FROM->TO states to get access to a shared flow. */ fun setup( duration: Duration, - stepFlow: Flow<TransitionStep>, - ) = SharedFlowBuilder(duration, stepFlow) + from: KeyguardState?, + to: KeyguardState?, + ): FlowBuilder { + if (from == null && to == null) { + throw IllegalArgumentException("from and to are both null") + } + + return FlowBuilder(duration, Edge(from, to)) + } - inner class SharedFlowBuilder( + inner class FlowBuilder( private val transitionDuration: Duration, - private val stepFlow: Flow<TransitionStep>, + private val edge: Edge, ) { /** * Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted @@ -115,20 +149,21 @@ constructor( }?.let { onStep(interpolator.getInterpolation(it)) } } - return stepFlow + return getOrCreateFlow(edge) .map { step -> - val value = - when (step.transitionState) { - STARTED -> stepToValue(step) - RUNNING -> stepToValue(step) - CANCELED -> onCancel?.invoke() - FINISHED -> onFinish?.invoke() - } - logger.logTransitionStep(name, step, value) - value + StateToValue( + step.transitionState, + when (step.transitionState) { + STARTED -> stepToValue(step) + RUNNING -> stepToValue(step) + CANCELED -> onCancel?.invoke() + FINISHED -> onFinish?.invoke() + } + ) + .also { logger.logTransitionStep(name, step, it.value) } } - .filterNotNull() .distinctUntilChanged() + .mapNotNull { stateToValue -> stateToValue.value } } /** @@ -138,4 +173,14 @@ constructor( return sharedFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value }) } } + + data class Edge( + val from: KeyguardState?, + val to: KeyguardState?, + ) + + data class StateToValue( + val transitionState: TransitionState, + val value: Float?, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index 05fe0b25381d..bf763b4e1f99 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -16,13 +16,20 @@ package com.android.systemui.keyguard.ui.binder +import android.animation.Animator +import android.animation.ValueAnimator +import android.transition.Transition import android.transition.TransitionManager +import android.transition.TransitionSet +import android.transition.TransitionValues +import android.view.ViewGroup import androidx.annotation.VisibleForTesting import androidx.constraintlayout.helper.widget.Layer import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection @@ -32,6 +39,8 @@ import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.res.R import kotlinx.coroutines.launch +private const val KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS = 1000L + object KeyguardClockViewBinder { @JvmStatic fun bind( @@ -69,7 +78,13 @@ object KeyguardClockViewBinder { launch { if (!migrateClocksToBlueprint()) return@launch viewModel.clockShouldBeCentered.collect { - applyConstraints(clockSection, keyguardRootView, true) + viewModel.clock?.let { + if (it.largeClock.config.hasCustomPositionUpdatedAnimation) { + playClockCenteringAnimation(clockSection, keyguardRootView, it) + } else { + applyConstraints(clockSection, keyguardRootView, true) + } + } } } } @@ -125,12 +140,84 @@ object KeyguardClockViewBinder { clockSection: ClockSection, rootView: ConstraintLayout, animated: Boolean, + set: TransitionSet? = null, ) { val constraintSet = ConstraintSet().apply { clone(rootView) } clockSection.applyConstraints(constraintSet) if (animated) { - TransitionManager.beginDelayedTransition(rootView) + set?.let { TransitionManager.beginDelayedTransition(rootView, it) } + ?: run { TransitionManager.beginDelayedTransition(rootView) } } constraintSet.applyTo(rootView) } + + private fun playClockCenteringAnimation( + clockSection: ClockSection, + keyguardRootView: ConstraintLayout, + clock: ClockController, + ) { + // Find the clock, so we can exclude it from this transition. + val clockView = clock.largeClock.view + val set = TransitionSet() + val adapter = SplitShadeTransitionAdapter(clock) + adapter.setInterpolator(Interpolators.LINEAR) + adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS) + adapter.addTarget(clockView) + set.addTransition(adapter) + applyConstraints(clockSection, keyguardRootView, true, set) + } + + internal class SplitShadeTransitionAdapter + @VisibleForTesting + constructor(private val clock: ClockController) : Transition() { + private fun captureValues(transitionValues: TransitionValues) { + transitionValues.values[PROP_BOUNDS_LEFT] = transitionValues.view.left + val locationInWindowTmp = IntArray(2) + transitionValues.view.getLocationInWindow(locationInWindowTmp) + transitionValues.values[PROP_X_IN_WINDOW] = locationInWindowTmp[0] + } + + override fun captureEndValues(transitionValues: TransitionValues) { + captureValues(transitionValues) + } + + override fun captureStartValues(transitionValues: TransitionValues) { + captureValues(transitionValues) + } + + override fun createAnimator( + sceneRoot: ViewGroup, + startValues: TransitionValues?, + endValues: TransitionValues? + ): Animator? { + if (startValues == null || endValues == null) { + return null + } + val anim = ValueAnimator.ofFloat(0f, 1f) + val fromLeft = startValues.values[PROP_BOUNDS_LEFT] as Int + val fromWindowX = startValues.values[PROP_X_IN_WINDOW] as Int + val toWindowX = endValues.values[PROP_X_IN_WINDOW] as Int + // Using windowX, to determine direction, instead of left, as in RTL the difference of + // toLeft - fromLeft is always positive, even when moving left. + val direction = if (toWindowX - fromWindowX > 0) 1 else -1 + anim.addUpdateListener { animation: ValueAnimator -> + clock.largeClock.animations.onPositionUpdated( + fromLeft, + direction, + animation.animatedFraction + ) + } + return anim + } + + override fun getTransitionProperties(): Array<String> { + return TRANSITION_PROPERTIES + } + + companion object { + private const val PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft" + private const val PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow" + private val TRANSITION_PROPERTIES = arrayOf(PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 92270ad31664..81ce8f04d302 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -23,6 +23,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel @@ -42,6 +43,7 @@ object KeyguardSmartspaceViewBinder { keyguardRootView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { + if (!migrateClocksToBlueprint()) return@launch clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay -> if (hasCustomWeatherDataDisplay) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt index f2b28d964314..f33aed03856c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt @@ -29,4 +29,8 @@ class KeyguardRootView( ConstraintLayout( context, attrs, - ) + ) { + init { + clipChildren = false + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt index 0f8e67340cc7..756a4cca69d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt @@ -23,11 +23,13 @@ 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.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlags +import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController @@ -35,6 +37,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCa import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel +import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -53,6 +56,7 @@ constructor( notificationStackSizeCalculator: NotificationStackSizeCalculator, private val smartspaceViewModel: KeyguardSmartspaceViewModel, @Main mainDispatcher: CoroutineDispatcher, + private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>, ) : NotificationStackScrollLayoutSection( context, @@ -72,7 +76,13 @@ constructor( } constraintSet.apply { val splitShadeTopMargin = - context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + if (centralizedStatusBarDimensRefactor()) { + largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + } else { + context.resources.getDimensionPixelSize( + R.dimen.large_screen_shade_header_height + ) + } connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin) connect(R.id.nssl_placeholder, START, PARENT_ID, START) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt index a8e3be79fc5a..b4b48a8fb932 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -38,14 +37,14 @@ import kotlinx.coroutines.flow.flatMapLatest class AlternateBouncerToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD), + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt index 5d6b0cebf959..3737e6fdf13e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.keyguard.shared.model.ScrimAlpha @@ -37,14 +36,14 @@ import kotlinx.coroutines.flow.Flow class AlternateBouncerToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, bouncerToGoneFlows: BouncerToGoneFlows, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = TO_GONE_DURATION, - stepFlow = interactor.transition(ALTERNATE_BOUNCER, KeyguardState.GONE), + from = ALTERNATE_BOUNCER, + to = KeyguardState.GONE, ) /** Scrim alpha values */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt index 8e729f76e096..2526f0aec796 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import android.graphics.Color import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager @@ -37,7 +36,6 @@ class AlternateBouncerViewModel @Inject constructor( private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, - transitionInteractor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: @@ -46,7 +44,8 @@ constructor( animationFlow .setup( duration = TRANSITION_DURATION_MS, - stepFlow = transitionInteractor.anyStateToAlternateBouncerTransition, + from = null, + to = ALTERNATE_BOUNCER, ) .sharedFlow( duration = TRANSITION_DURATION_MS, @@ -60,7 +59,8 @@ constructor( animationFlow .setup( TRANSITION_DURATION_MS, - transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER), + from = ALTERNATE_BOUNCER, + to = null, ) .sharedFlow( duration = TRANSITION_DURATION_MS, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt index 2b145216cb80..b92a9a08987a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -31,14 +30,14 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi class AodToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromAodTransitionInteractor.TO_GONE_DURATION, - stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.GONE), + from = KeyguardState.AOD, + to = KeyguardState.GONE, ) override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt index 5e552e1fe00f..266fd02d5bbf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.flatMapLatest class AodToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { @@ -45,7 +44,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.aodToLockscreenTransition, + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, ) /** Ensure alpha is set to be visible */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt index d283af359b06..105a7ed52311 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -29,13 +28,13 @@ import javax.inject.Inject class AodToOccludedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION, - stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED), + from = KeyguardState.AOD, + to = KeyguardState.OCCLUDED, ) override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt index 41dc15778b0c..924fc5d0333f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt @@ -21,7 +21,6 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.ScrimAlpha @@ -41,7 +40,6 @@ import kotlinx.coroutines.flow.map class BouncerToGoneFlows @Inject constructor( - private val interactor: KeyguardTransitionInteractor, private val statusBarStateController: SysuiStatusBarStateController, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>, @@ -76,7 +74,8 @@ constructor( val transitionAnimation = animationFlow.setup( duration = duration, - stepFlow = interactor.transition(fromState, GONE) + from = fromState, + to = GONE, ) return shadeInteractor.shadeExpansion.flatMapLatest { shadeExpansion -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt index 0b34326bc83d..e4610c15a3d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -34,13 +34,13 @@ import kotlinx.coroutines.flow.Flow class DozingToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION, - stepFlow = interactor.dozingToLockscreenTransition, + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt index 8bcf3f8a76d9..67568e12a4a1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class DreamingHostedToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.dreamingLockscreenHostedToLockscreenTransition, + from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, + to = KeyguardState.LOCKSCREEN, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt index 5f620afe2dea..ead2d48240c0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt @@ -22,6 +22,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsIntera import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -52,7 +53,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = keyguardTransitionInteractor.dreamingToLockscreenTransition, + from = KeyguardState.DREAMING, + to = KeyguardState.LOCKSCREEN, ) val transitionEnded = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt index 3f27eb0c73e3..ba04fd3741a4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt @@ -20,7 +20,7 @@ import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.flatMapLatest class GoneToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { @@ -44,7 +43,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_AOD_DURATION, - stepFlow = interactor.goneToAodTransition, + from = KeyguardState.GONE, + to = KeyguardState.AOD, ) /** y-translation from the top of the screen for AOD */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt index bba790abe807..b52746364a8b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -32,14 +32,14 @@ import kotlinx.coroutines.flow.Flow class GoneToDreamingLockscreenHostedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_DURATION, - stepFlow = interactor.goneToDreamingLockscreenHostedTransition, + from = KeyguardState.GONE, + to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, ) /** Lockscreen views alpha - hide immediately */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt index 6762ba6298a5..102242a4a7b0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -30,14 +30,14 @@ import kotlinx.coroutines.flow.Flow class GoneToDreamingTransitionViewModel @Inject constructor( - private val interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_DURATION, - stepFlow = interactor.goneToDreamingTransition, + from = KeyguardState.GONE, + to = KeyguardState.DREAMING, ) /** Lockscreen views y-translation */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt index adae8abfb9c3..793abb444f31 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class GoneToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.goneToLockscreenTransition + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt index 65614f47b120..7bf51a7d3d54 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.flatMapLatest class LockscreenToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, shadeDependentFlows: ShadeDependentFlows, animationFlow: KeyguardTransitionAnimationFlow, @@ -45,7 +44,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.lockscreenToAodTransition, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt index accb20c91f98..4c0cd2f58eb7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToDozingTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DOZING_DURATION, - stepFlow = interactor.lockscreenToDozingTransition + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt index c649b12b71e4..19b9cf4733f9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -28,14 +28,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToDreamingHostedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_HOSTED_DURATION, - stepFlow = interactor.lockscreenToDreamingLockscreenHostedTransition + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt index 7f75b547d717..13522a6742ac 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -34,14 +34,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToDreamingTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, shadeDependentFlows: ShadeDependentFlows, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_DURATION, - stepFlow = interactor.lockscreenToDreamingTransition, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, ) /** Lockscreen views y-translation */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt index 9e197138d0b2..a26ef0755123 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -35,14 +34,14 @@ import kotlinx.coroutines.flow.Flow class LockscreenToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION, - stepFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE), + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt index 9db0b775cd40..dd6652e69792 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt @@ -20,7 +20,7 @@ import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R @@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.flatMapLatest class LockscreenToOccludedTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, shadeDependentFlows: ShadeDependentFlows, configurationInteractor: ConfigurationInteractor, animationFlow: KeyguardTransitionAnimationFlow, @@ -46,7 +45,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_OCCLUDED_DURATION, - stepFlow = interactor.lockscreenToOccludedTransition, + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.OCCLUDED, ) /** Lockscreen views alpha */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt index 52e3257f8e18..ce47f3c67b21 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -26,7 +25,6 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map /** * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to @@ -37,21 +35,21 @@ import kotlinx.coroutines.flow.map class LockscreenToPrimaryBouncerTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, shadeDependentFlows: ShadeDependentFlows, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - stepFlow = - interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER), + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.PRIMARY_BOUNCER, ) val shortcutsAlpha: Flow<Float> = - interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER).map { - 1 - it.value - } + transitionAnimation.sharedFlow( + duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, + onStep = { 1f - it } + ) override val deviceEntryParentViewAlpha: Flow<Float> = shadeDependentFlows.transitionFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt index ed5e83c44640..07c114163326 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -35,14 +34,14 @@ import kotlinx.coroutines.flow.flatMapLatest class OccludedToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromOccludedTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD), + from = KeyguardState.OCCLUDED, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt index 4c24f83200b2..90195bd343b7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt @@ -21,7 +21,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.flatMapLatest class OccludedToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, configurationInteractor: ConfigurationInteractor, animationFlow: KeyguardTransitionAnimationFlow, @@ -50,7 +49,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - stepFlow = interactor.occludedToLockscreenTransition, + from = KeyguardState.OCCLUDED, + to = KeyguardState.LOCKSCREEN, ) /** Lockscreen views y-translation */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt index 93482ea162bb..74094bea140a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -27,14 +27,14 @@ import kotlinx.coroutines.flow.Flow class OffToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = animationFlow.setup( duration = 250.milliseconds, - stepFlow = interactor.offToLockscreenTransition + from = KeyguardState.OFF, + to = KeyguardState.LOCKSCREEN, ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt index b0e2aa2d4765..cd8e2f12cb19 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -39,14 +38,14 @@ import kotlinx.coroutines.flow.flatMapLatest class PrimaryBouncerToAodTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION, - stepFlow = interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD), + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.AOD, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt index 9dbe97fd1c20..4f28b4608e49 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -22,7 +22,6 @@ import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.ScrimAlpha @@ -44,7 +43,6 @@ import kotlinx.coroutines.flow.flatMapLatest class PrimaryBouncerToGoneTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, private val statusBarStateController: SysuiStatusBarStateController, private val primaryBouncerInteractor: PrimaryBouncerInteractor, keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>, @@ -55,7 +53,8 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_GONE_DURATION, - stepFlow = interactor.transition(PRIMARY_BOUNCER, GONE) + from = PRIMARY_BOUNCER, + to = GONE, ) private var leaveShadeOpen: Boolean = false diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt index b2eed60e0a9e..284a134f73c7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -28,7 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map /** * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to @@ -39,15 +37,14 @@ import kotlinx.coroutines.flow.map class PrimaryBouncerToLockscreenTransitionViewModel @Inject constructor( - interactor: KeyguardTransitionInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION, - stepFlow = - interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN), + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.LOCKSCREEN, ) val deviceEntryBackgroundViewAlpha: Flow<Float> = @@ -60,9 +57,10 @@ constructor( } val shortcutsAlpha: Flow<Float> = - interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN).map { - it.value - } + transitionAnimation.sharedFlow( + duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION, + onStep = { it } + ) override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index 0385aeba32b7..523414cfddbf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -1153,7 +1153,7 @@ constructor( qsExpansion > 0.4f && onLockscreen -> LOCATION_QS onLockscreen && isSplitShadeExpanding() -> LOCATION_QS onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS - // TODO(b/308813166): revisit logic once interactions between the hub and + // TODO(b/311234666): revisit logic once interactions between the hub and // shade/keyguard state are finalized isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java index 0320dec0580f..092f1ed7d498 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java @@ -302,9 +302,6 @@ public class NavigationBarControllerImpl implements final NavigationBarView navBarView = getNavigationBarView(displayId); if (navBarView != null) { navBarView.showPinningEnterExitToast(entering); - } else if (displayId == mDisplayTracker.getDefaultDisplayId() - && mTaskbarDelegate.isInitialized()) { - mTaskbarDelegate.showPinningEnterExitToast(entering); } } @@ -314,9 +311,6 @@ public class NavigationBarControllerImpl implements final NavigationBarView navBarView = getNavigationBarView(displayId); if (navBarView != null) { navBarView.showPinningEscapeToast(); - } else if (displayId == mDisplayTracker.getDefaultDisplayId() - && mTaskbarDelegate.isInitialized()) { - mTaskbarDelegate.showPinningEscapeToast(); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index 62c7343d3fe5..01672875fd44 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -504,6 +504,11 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, } @Override + public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) { + mOverviewProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable); + } + + @Override public void showPinningEnterExitToast(boolean entering) { updateSysuiFlags(); if (mScreenPinningNotify == null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 064423768cd3..a3b92541d593 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -18,6 +18,8 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Path; @@ -30,6 +32,7 @@ import android.widget.FrameLayout; import com.android.systemui.Dumpable; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.res.R; +import com.android.systemui.shade.LargeScreenHeaderHelper; import com.android.systemui.shade.TouchLogger; import com.android.systemui.util.LargeScreenUtils; @@ -162,8 +165,12 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { QuickStatusBarHeaderController quickStatusBarHeaderController) { int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext); if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) { - topPadding = mContext.getResources() - .getDimensionPixelSize(R.dimen.large_screen_shade_header_height); + topPadding = + centralizedStatusBarDimensRefactor() + ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext) + : mContext.getResources() + .getDimensionPixelSize( + R.dimen.large_screen_shade_header_height); } mQSPanelContainer.setPaddingRelative( mQSPanelContainer.getPaddingStart(), diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 45917e85d80e..fd53423e7f22 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -1037,6 +1037,19 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) { + try { + if (mOverviewProxy != null) { + mOverviewProxy.onNavigationBarLumaSamplingEnabled(displayId, enable); + } else { + Log.e(TAG_OPS, "Failed to get overview proxy to enable/disable nav bar luma" + + "sampling"); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call onNavigationBarLumaSamplingEnabled()", e); + } + } + private void updateEnabledState() { final int currentUser = mUserTracker.getUserId(); mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent, diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index bd43307dba5a..7aa0dadba16c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -32,6 +32,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -143,6 +144,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList channel.enableVibration(true); mNotificationManager.createNotificationChannel(channel); + int currentUid = Process.myUid(); int currentUserId = mUserContextTracker.getUserContext().getUserId(); UserHandle currentUser = new UserHandle(currentUserId); switch (action) { @@ -166,7 +168,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList mRecorder = new ScreenMediaRecorder( mUserContextTracker.getUserContext(), mMainHandler, - currentUserId, + currentUid, mAudioSource, captureTarget, this diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 3aab3bf62809..a170d0dac100 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -83,7 +83,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { private Surface mInputSurface; private VirtualDisplay mVirtualDisplay; private MediaRecorder mMediaRecorder; - private int mUser; + private int mUid; private ScreenRecordingMuxer mMuxer; private ScreenInternalAudioRecorder mAudio; private ScreenRecordingAudioSource mAudioSource; @@ -94,12 +94,12 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { ScreenMediaRecorderListener mListener; public ScreenMediaRecorder(Context context, Handler handler, - int user, ScreenRecordingAudioSource audioSource, + int uid, ScreenRecordingAudioSource audioSource, MediaProjectionCaptureTarget captureRegion, ScreenMediaRecorderListener listener) { mContext = context; mHandler = handler; - mUser = user; + mUid = uid; mCaptureRegion = captureRegion; mListener = listener; mAudioSource = audioSource; @@ -111,7 +111,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { IMediaProjectionManager mediaService = IMediaProjectionManager.Stub.asInterface(b); IMediaProjection proj = null; - proj = mediaService.createProjection(mUser, mContext.getPackageName(), + proj = mediaService.createProjection(mUid, mContext.getPackageName(), MediaProjectionManager.TYPE_SCREEN_CAPTURE, false); IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder()); if (mCaptureRegion != null) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 3be60b74af21..782d6519468c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -17,6 +17,8 @@ package com.android.systemui.shade import android.content.Context +import android.os.PowerManager +import android.os.SystemClock import android.view.GestureDetector import android.view.MotionEvent import android.view.View @@ -44,6 +46,7 @@ constructor( private val communalViewModel: CommunalViewModel, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val shadeInteractor: ShadeInteractor, + private val powerManager: PowerManager, ) { /** The container view for the hub. This will not be initialized until [initView] is called. */ private lateinit var communalContainerView: View @@ -157,7 +160,7 @@ constructor( // If the hub is fully visible, send all touch events to it. val communalVisible = hubShowing && !hubOccluded if (communalVisible) { - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a gesture // may return false from dispatchTouchEvent. return true @@ -175,7 +178,7 @@ constructor( x >= communalContainerView.width - edgeSwipeRegionWidth if (inOpeningSwipeRegion && !hubOccluded) { isTrackingOpenGesture = true - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a // gesture may return false from dispatchTouchEvent. return true @@ -184,7 +187,7 @@ constructor( if (isUp || isCancel) { isTrackingOpenGesture = false } - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a gesture // may return false from dispatchTouchEvent. return true @@ -192,4 +195,17 @@ constructor( return false } + + /** + * Dispatches the touch event to the communal container and sends a user activity event to reset + * the screen timeout. + */ + private fun dispatchTouchEvent(ev: MotionEvent) { + communalContainerView.dispatchTouchEvent(ev) + powerManager.userActivity( + SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_TOUCH, + 0 + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt new file mode 100644 index 000000000000..c74f038ebea4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.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.shade + +import android.content.Context +import com.android.internal.policy.SystemBarUtils +import com.android.systemui.res.R +import javax.inject.Inject +import kotlin.math.max + +class LargeScreenHeaderHelper @Inject constructor(private val context: Context) { + + fun getLargeScreenHeaderHeight(): Int = getLargeScreenHeaderHeight(context) + + companion object { + @JvmStatic + fun getLargeScreenHeaderHeight(context: Context): Int { + val defaultHeight = + context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + val statusBarHeight = SystemBarUtils.getStatusBarHeight(context) + // Height has to be at least as tall as the status bar, as the status bar height takes + // into account display cutouts. + return max(defaultHeight, statusBarHeight) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 6f4a1e7754f5..aeccf0031419 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -27,6 +27,7 @@ import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.Flags.keyguardBottomAreaRefactor; import static com.android.systemui.Flags.migrateClocksToBlueprint; import static com.android.systemui.Flags.predictiveBackAnimateShade; +import static com.android.systemui.Flags.smartspaceRelocateToBottom; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -1214,7 +1215,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump .setMaxLengthSeconds(0.4f).build(); mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext()); mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext()); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mView.getContext(), mResources); mIndicationBottomPadding = mResources.getDimensionPixelSize( R.dimen.keyguard_indication_bottom_padding); int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext()); @@ -1428,7 +1429,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump int index = mView.indexOfChild(mKeyguardBottomArea); mView.removeView(mKeyguardBottomArea); KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; - setKeyguardBottomArea(mKeyguardBottomAreaViewControllerProvider.get().getView()); + KeyguardBottomAreaViewController keyguardBottomAreaViewController = + mKeyguardBottomAreaViewControllerProvider.get(); + if (smartspaceRelocateToBottom()) { + keyguardBottomAreaViewController.init(); + } + setKeyguardBottomArea(keyguardBottomAreaViewController.getView()); mKeyguardBottomArea.initFrom(oldBottomArea); mView.addView(mKeyguardBottomArea, index); @@ -1751,14 +1757,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } else { layout = mNotificationContainerParent; } - - if (migrateClocksToBlueprint()) { - mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered); - } else { - mKeyguardStatusViewController.updateAlignment( - layout, mSplitShadeEnabled, shouldBeCentered, animate); - mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); - } + mKeyguardStatusViewController.updateAlignment( + layout, mSplitShadeEnabled, shouldBeCentered, animate); + mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); } private boolean shouldKeyguardStatusViewBeCentered() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index 9c8a286b2918..84cad1d16d73 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.lifecycle.lifecycleScope +import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags @@ -47,10 +48,11 @@ 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 dagger.Lazy import java.util.function.Consumer import javax.inject.Inject import kotlin.reflect.KMutableProperty0 +import kotlinx.coroutines.launch @VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L @@ -67,7 +69,8 @@ class NotificationsQSContainerController @Inject constructor( private val featureFlags: FeatureFlags, private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController, - private val splitShadeStateController: SplitShadeStateController + private val splitShadeStateController: SplitShadeStateController, + private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>, ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController { private var splitShadeEnabled = false @@ -186,7 +189,11 @@ class NotificationsQSContainerController @Inject constructor( } private fun calculateLargeShadeHeaderHeight(): Int { - return resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + return if (centralizedStatusBarDimensRefactor()) { + largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + } else { + resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + } } private fun calculateShadeHeaderHeight(): Int { diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 1dff99d53078..f3e9c7503626 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -20,6 +20,7 @@ package com.android.systemui.shade; import static android.view.WindowInsets.Type.ime; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS; import static com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE; @@ -126,6 +127,7 @@ public class QuickSettingsController implements Dumpable { private final Lazy<NotificationPanelViewController> mPanelViewControllerLazy; private final NotificationPanelView mPanelView; + private final Lazy<LargeScreenHeaderHelper> mLargeScreenHeaderHelperLazy; private final KeyguardStatusBarView mKeyguardStatusBar; private final FrameLayout mQsFrame; @@ -344,10 +346,12 @@ public class QuickSettingsController implements Dumpable { ActiveNotificationsInteractor activeNotificationsInteractor, JavaAdapter javaAdapter, CastController castController, - SplitShadeStateController splitShadeStateController + SplitShadeStateController splitShadeStateController, + Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy ) { mPanelViewControllerLazy = panelViewControllerLazy; mPanelView = panelView; + mLargeScreenHeaderHelperLazy = largeScreenHeaderHelperLazy; mQsFrame = mPanelView.findViewById(R.id.qs_frame); mKeyguardStatusBar = mPanelView.findViewById(R.id.keyguard_header); mResources = mPanelView.getResources(); @@ -449,7 +453,10 @@ public class QuickSettingsController implements Dumpable { mUseLargeScreenShadeHeader = LargeScreenUtils.shouldUseLargeScreenShadeHeader(mPanelView.getResources()); mLargeScreenShadeHeaderHeight = - mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); + centralizedStatusBarDimensRefactor() + ? mLargeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + : mResources.getDimensionPixelSize( + R.dimen.large_screen_shade_header_height); int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top); mShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 2460a3314be5..a66bacd237be 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -38,6 +38,7 @@ import androidx.core.view.doOnLayout import com.android.app.animation.Interpolators import com.android.settingslib.Utils import com.android.systemui.Dumpable +import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController @@ -432,6 +433,9 @@ constructor( changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints() } + if (centralizedStatusBarDimensRefactor()) { + view.setPadding(view.paddingLeft, sbInsets.top, view.paddingRight, view.paddingBottom) + } view.updateAllConstraints(changes) updateBatteryMode() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 39b7930ed386..6e8507422fbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -253,6 +253,8 @@ constructor( initialHeight: Int? = null ) : View(context, attrs) { + private val logString = this::class.simpleName!! + "@" + hashCode() + /** Listener that is called if the scrim's opaqueness changes */ var isScrimOpaqueChangedListener: Consumer<Boolean>? = null @@ -267,13 +269,13 @@ constructor( if (field != value) { field = value if (value <= 0.0f || value >= 1.0f) { - scrimLogger?.d(TAG, "revealAmount", "$value on ${logString()}") + scrimLogger?.d(TAG, "revealAmount", "$value on $logString") } revealEffect.setRevealAmountOnScrim(value, this) updateScrimOpaque() Trace.traceCounter( Trace.TRACE_TAG_APP, - "light_reveal_amount", + "light_reveal_amount $logString", (field * 100).toInt() ) invalidate() @@ -290,7 +292,7 @@ constructor( field = value revealEffect.setRevealAmountOnScrim(revealAmount, this) - scrimLogger?.d(TAG, "revealEffect", "$value on ${logString()}") + scrimLogger?.d(TAG, "revealEffect", "$value on $logString") invalidate() } } @@ -350,7 +352,7 @@ constructor( if (field != value) { field = value isScrimOpaqueChangedListener?.accept(field) - scrimLogger?.d(TAG, "isScrimOpaque", "$value on ${logString()}") + scrimLogger?.d(TAG, "isScrimOpaque", "$value on $logString") } } @@ -368,13 +370,13 @@ constructor( override fun setAlpha(alpha: Float) { super.setAlpha(alpha) - scrimLogger?.d(TAG, "alpha", "$alpha on ${logString()}") + scrimLogger?.d(TAG, "alpha", "$alpha on $logString") updateScrimOpaque() } override fun setVisibility(visibility: Int) { super.setVisibility(visibility) - scrimLogger?.d(TAG, "visibility", "$visibility on ${logString()}") + scrimLogger?.d(TAG, "visibility", "$visibility on $logString") updateScrimOpaque() } @@ -467,8 +469,4 @@ constructor( PorterDuff.Mode.MULTIPLY ) } - - private fun logString(): String { - return this::class.simpleName!! + "@" + hashCode() - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 9b8dd0b75a24..24ac70e63e46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -15,17 +15,17 @@ */ package com.android.systemui.statusbar; +import static android.app.Flags.keyguardPrivateNotifications; import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED; import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; +import static android.os.Flags.allowPrivateProfile; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_NULL; import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; -import static android.app.Flags.keyguardPrivateNotifications; -import static android.os.Flags.allowPrivateProfile; import static com.android.systemui.DejankUtils.whitelistIpcs; @@ -42,9 +42,8 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; import android.database.ContentObserver; +import android.database.ExecutorContentObserver; import android.net.Uri; -import android.os.Handler; -import android.os.HandlerExecutor; import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; @@ -79,17 +78,17 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.settings.SecureSettings; +import dagger.Lazy; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.Executor; import java.util.Objects; +import java.util.concurrent.Executor; import javax.inject.Inject; -import dagger.Lazy; - /** * Handles keeping track of the current user, profiles, and various things related to hiding * contents, redacting notifications, and the lockscreen. @@ -228,7 +227,7 @@ public class NotificationLockscreenUserManagerImpl implements updateCurrentProfilesCache(); if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); - mBackgroundHandler.post(() -> { + mBackgroundExecutor.execute(() -> { initValuesForUser(userId); }); } @@ -289,8 +288,7 @@ public class NotificationLockscreenUserManagerImpl implements }; protected final Context mContext; - private final Handler mMainHandler; - private final Handler mBackgroundHandler; + private final Executor mMainExecutor; private final Executor mBackgroundExecutor; /** The current user and its profiles (possibly including a communal profile). */ protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>(); @@ -313,8 +311,7 @@ public class NotificationLockscreenUserManagerImpl implements Lazy<OverviewProxyService> overviewProxyServiceLazy, KeyguardManager keyguardManager, StatusBarStateController statusBarStateController, - @Main Handler mainHandler, - @Background Handler backgroundHandler, + @Main Executor mainExecutor, @Background Executor backgroundExecutor, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, @@ -323,8 +320,7 @@ public class NotificationLockscreenUserManagerImpl implements LockPatternUtils lockPatternUtils, FeatureFlagsClassic featureFlags) { mContext = context; - mMainHandler = mainHandler; - mBackgroundHandler = backgroundHandler; + mMainExecutor = mainExecutor; mBackgroundExecutor = backgroundExecutor; mDevicePolicyManager = devicePolicyManager; mUserManager = userManager; @@ -362,10 +358,10 @@ public class NotificationLockscreenUserManagerImpl implements } private void init() { - mLockscreenSettingsObserver = new ContentObserver( + mLockscreenSettingsObserver = new ExecutorContentObserver( mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD) - ? mBackgroundHandler - : mMainHandler) { + ? mBackgroundExecutor + : mMainExecutor) { @Override public void onChange(boolean selfChange, Collection<Uri> uris, int flags) { @@ -412,7 +408,7 @@ public class NotificationLockscreenUserManagerImpl implements } }; - mSettingsObserver = new ContentObserver(mMainHandler) { + mSettingsObserver = new ExecutorContentObserver(mMainExecutor) { @Override public void onChange(boolean selfChange) { updateLockscreenNotificationSetting(); @@ -468,14 +464,14 @@ public class NotificationLockscreenUserManagerImpl implements mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null, Context.RECEIVER_EXPORTED_UNAUDITED); - mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler)); + mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late updateCurrentProfilesCache(); if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { // Set up - mBackgroundHandler.post(() -> { + mBackgroundExecutor.execute(() -> { @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers(); for (int i = users.size() - 1; i >= 0; i--) { initValuesForUser(users.get(i).id); @@ -796,7 +792,7 @@ public class NotificationLockscreenUserManagerImpl implements } } } - mMainHandler.post(() -> { + mMainExecutor.execute(() -> { for (UserChangedListener listener : mListeners) { listener.onCurrentProfilesChanged(mCurrentProfiles); } @@ -895,7 +891,7 @@ public class NotificationLockscreenUserManagerImpl implements private void notifyNotificationStateChanged() { if (!Looper.getMainLooper().isCurrentThread()) { - mMainHandler.post(() -> { + mMainExecutor.execute(() -> { for (NotificationStateChangedListener listener : mNotifStateChangedListeners) { listener.onNotificationStateChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt index 625fdc1c12f8..4b8fb1e5a0ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt @@ -18,12 +18,15 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.Context +import com.android.systemui.Flags.centralizedStatusBarDimensRefactor import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.res.R +import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.statusbar.policy.SplitShadeStateController +import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -45,6 +48,7 @@ constructor( private val splitShadeStateController: SplitShadeStateController, keyguardInteractor: KeyguardInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, + largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>, ) { private val _topPosition = MutableStateFlow(0f) @@ -72,7 +76,11 @@ constructor( getDimensionPixelSize(R.dimen.notification_panel_margin_bottom), marginTop = getDimensionPixelSize(R.dimen.notification_panel_margin_top), marginTopLargeScreen = - getDimensionPixelSize(R.dimen.large_screen_shade_header_height), + if (centralizedStatusBarDimensRefactor()) { + largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight() + } else { + getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + }, keyguardSplitShadeTopMargin = getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin), ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt index 0bad47ecb2ae..8a45ec15b627 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt @@ -18,12 +18,23 @@ package com.android.systemui.statusbar.phone import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags +import com.android.systemui.Flags.smartspaceRelocateToBottom +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import com.android.systemui.res.R +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.util.ViewController import javax.inject.Inject class KeyguardBottomAreaViewController - @Inject constructor(view: KeyguardBottomAreaView, featureFlags: FeatureFlagsClassic) : - ViewController<KeyguardBottomAreaView> (view) { + @Inject constructor( + view: KeyguardBottomAreaView, + private val smartspaceController: LockscreenSmartspaceController, + featureFlags: FeatureFlagsClassic +) : ViewController<KeyguardBottomAreaView> (view) { + + private var smartspaceView: View? = null init { view.setIsLockscreenLandscapeEnabled( @@ -31,6 +42,14 @@ class KeyguardBottomAreaViewController } override fun onViewAttached() { + if (!smartspaceRelocateToBottom() || !smartspaceController.isEnabled()) { + return + } + + val ambientIndicationArea = mView.findViewById<View>(R.id.ambient_indication_container) + ambientIndicationArea?.visibility = View.GONE + + addSmartspaceView() } override fun onViewDetached() { @@ -40,4 +59,24 @@ class KeyguardBottomAreaViewController // TODO: remove this method. return mView } + + private fun addSmartspaceView() { + if (!smartspaceRelocateToBottom()) { + return + } + + val smartspaceContainer = mView.findViewById<View>(R.id.smartspace_container) + smartspaceContainer!!.visibility = View.VISIBLE + + smartspaceView = smartspaceController.buildAndConnectView(smartspaceContainer as ViewGroup) + val lp = LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + (smartspaceContainer as ViewGroup).addView(smartspaceView, 0, lp) + val startPadding = context.resources.getDimensionPixelSize( + R.dimen.below_clock_padding_start) + val endPadding = context.resources.getDimensionPixelSize( + R.dimen.below_clock_padding_end) + smartspaceView?.setPaddingRelative(startPadding, 0, endPadding, 0) +// mKeyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 0a03af7d9387..ca3e3c629619 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -16,10 +16,12 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale; import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate; +import android.content.Context; import android.content.res.Resources; import android.util.MathUtils; @@ -30,6 +32,7 @@ import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.Logger; import com.android.systemui.log.dagger.KeyguardClockLog; import com.android.systemui.res.R; +import com.android.systemui.shade.LargeScreenHeaderHelper; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView; @@ -160,14 +163,14 @@ public class KeyguardClockPositionAlgorithm { mLogger = new Logger(logBuffer, TAG); } - /** - * Refreshes the dimension values. - */ - public void loadDimens(Resources res) { - mStatusViewBottomMargin = res.getDimensionPixelSize( - R.dimen.keyguard_status_view_bottom_margin); + /** Refreshes the dimension values. */ + public void loadDimens(Context context, Resources res) { + mStatusViewBottomMargin = + res.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin); mSplitShadeTopNotificationsMargin = - res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); + centralizedStatusBarDimensRefactor() + ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(context) + : res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); mSplitShadeTargetTopMargin = res.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 7cc08887ae52..769145923886 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor; import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection; import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard; @@ -130,6 +131,9 @@ public class KeyguardStatusBarView extends RelativeLayout { mUserSwitcherContainer = findViewById(R.id.user_switcher_container); mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot); loadDimens(); + if (!centralizedStatusBarDimensRefactor()) { + setGravity(Gravity.CENTER_VERTICAL); + } } /** @@ -307,7 +311,8 @@ public class KeyguardStatusBarView extends RelativeLayout { final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled) ? Math.max(mMinDotWidth, mPadding.right) : mPadding.right; - setPadding(minLeft, waterfallTop, minRight, 0); + int top = centralizedStatusBarDimensRefactor() ? waterfallTop + mPadding.top : waterfallTop; + setPadding(minLeft, top, minRight, 0); } private boolean updateLayoutParamsNoCutout() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 30a445f7ce4a..703b3c65029a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -118,15 +118,25 @@ public class StatusBarSignalPolicy implements SignalCallback, private void updateVpn() { boolean vpnVisible = mSecurityController.isVpnEnabled(); - int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); + int vpnIconId = currentVpnIconId( + mSecurityController.isVpnBranded(), + mSecurityController.isVpnValidated()); mIconController.setIcon(mSlotVpn, vpnIconId, mContext.getResources().getString(R.string.accessibility_vpn_on)); mIconController.setIconVisibility(mSlotVpn, vpnVisible); } - private int currentVpnIconId(boolean isBranded) { - return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; + private int currentVpnIconId(boolean isBranded, boolean isValidated) { + if (isBranded) { + return isValidated + ? R.drawable.stat_sys_branded_vpn + : R.drawable.stat_sys_no_internet_branded_vpn; + } else { + return isValidated + ? R.drawable.stat_sys_vpn_ic + : R.drawable.stat_sys_no_internet_vpn_ic; + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt index 8fc8b2f31366..de46a5ed99d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.pipeline.satellite.data.prod import android.os.OutcomeReceiver import android.telephony.satellite.NtnSignalStrengthCallback import android.telephony.satellite.SatelliteManager -import android.telephony.satellite.SatelliteStateCallback +import android.telephony.satellite.SatelliteModemStateCallback import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -180,7 +180,7 @@ constructor( // By using the SupportedSatelliteManager here, we expect registration never to fail private fun connectionStateFlow(sm: SupportedSatelliteManager): Flow<SatelliteConnectionState> = conflatedCallbackFlow { - val cb = SatelliteStateCallback { state -> + val cb = SatelliteModemStateCallback { state -> trySend(SatelliteConnectionState.fromModemState(state)) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java index 3be14bc867a1..10bf0680b567 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java @@ -48,6 +48,8 @@ public interface SecurityController extends CallbackController<SecurityControlle boolean isNetworkLoggingEnabled(); boolean isVpnEnabled(); boolean isVpnRestricted(); + /** Whether the VPN network is validated. */ + boolean isVpnValidated(); /** Whether the VPN app should use branded VPN iconography. */ boolean isVpnBranded(); String getPrimaryVpnName(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 5d69f367d77e..9f4a90658b2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -15,6 +15,9 @@ */ package com.android.systemui.statusbar.policy; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; + import android.annotation.Nullable; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManager; @@ -32,7 +35,9 @@ import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.VpnManager; import android.os.Handler; @@ -76,7 +81,10 @@ public class SecurityControllerImpl implements SecurityController { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final NetworkRequest REQUEST = - new NetworkRequest.Builder().clearCapabilities().build(); + new NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(TRANSPORT_VPN) + .build(); private static final int NO_NETWORK = -1; private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED"; @@ -99,6 +107,8 @@ public class SecurityControllerImpl implements SecurityController { private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>(); private int mCurrentUserId; private int mVpnUserId; + @GuardedBy("mNetworkProperties") + private final SparseArray<NetworkProperties> mNetworkProperties = new SparseArray<>(); // Key: userId, Value: whether the user has CACerts installed // Needs to be cached here since the query has to be asynchronous @@ -162,6 +172,21 @@ public class SecurityControllerImpl implements SecurityController { pw.print(mCurrentVpns.valueAt(i).user); } pw.println("}"); + pw.print(" mNetworkProperties={"); + synchronized (mNetworkProperties) { + for (int i = 0; i < mNetworkProperties.size(); ++i) { + if (i > 0) { + pw.print(", "); + } + pw.print(mNetworkProperties.keyAt(i)); + pw.print("={"); + pw.print(mNetworkProperties.valueAt(i).interfaceName); + pw.print(", "); + pw.print(mNetworkProperties.valueAt(i).validated); + pw.print("}"); + } + } + pw.println("}"); } @Override @@ -304,6 +329,26 @@ public class SecurityControllerImpl implements SecurityController { } @Override + public boolean isVpnValidated() { + // Prioritize reporting the network status of the parent user. + final VpnConfig primaryVpnConfig = mCurrentVpns.get(mVpnUserId); + if (primaryVpnConfig != null) { + return getVpnValidationStatus(primaryVpnConfig); + } + // Identify any Unvalidated status in each active VPN network within other profiles. + for (int profileId : mUserManager.getEnabledProfileIds(mVpnUserId)) { + final VpnConfig vpnConfig = mCurrentVpns.get(profileId); + if (vpnConfig == null) { + continue; + } + if (!getVpnValidationStatus(vpnConfig)) { + return false; + } + } + return true; + } + + @Override public boolean hasCACertInCurrentUser() { Boolean hasCACerts = mHasCACerts.get(mCurrentUserId); return hasCACerts != null && hasCACerts.booleanValue(); @@ -493,11 +538,74 @@ public class SecurityControllerImpl implements SecurityController { @Override public void onLost(Network network) { if (DEBUG) Log.d(TAG, "onLost " + network.getNetId()); + synchronized (mNetworkProperties) { + mNetworkProperties.delete(network.getNetId()); + } updateState(); fireCallbacks(); }; + + + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + if (DEBUG) Log.d(TAG, "onCapabilitiesChanged " + network.getNetId()); + final NetworkProperties properties; + synchronized (mNetworkProperties) { + properties = mNetworkProperties.get(network.getNetId()); + } + // When a new network appears, the system first notifies the application about + // its capabilities through onCapabilitiesChanged. This initial notification + // will be skipped because the interface information is included in the + // subsequent onLinkPropertiesChanged call. After validating the network, the + // system might send another onCapabilitiesChanged notification if the network + // becomes validated. + if (properties == null) { + return; + } + final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED); + if (properties.validated != validated) { + properties.validated = validated; + fireCallbacks(); + } + } + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { + if (DEBUG) Log.d(TAG, "onLinkPropertiesChanged " + network.getNetId()); + final String interfaceName = linkProperties.getInterfaceName(); + if (interfaceName == null) { + Log.w(TAG, "onLinkPropertiesChanged event with null interface"); + return; + } + synchronized (mNetworkProperties) { + final NetworkProperties properties = mNetworkProperties.get(network.getNetId()); + if (properties == null) { + mNetworkProperties.put( + network.getNetId(), + new NetworkProperties(interfaceName, false)); + } else { + properties.interfaceName = interfaceName; + } + } + } }; + /** + * Retrieve the validation status of the VPN network associated with the given VpnConfig. + */ + private boolean getVpnValidationStatus(@NonNull VpnConfig vpnConfig) { + synchronized (mNetworkProperties) { + // Find the network has the same interface as the VpnConfig + for (int i = 0; i < mNetworkProperties.size(); ++i) { + if (mNetworkProperties.valueAt(i).interfaceName.equals(vpnConfig.interfaze)) { + return mNetworkProperties.valueAt(i).validated; + } + } + } + // If no matching network is found, consider it validated. + return true; + } + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) { @@ -508,4 +616,17 @@ public class SecurityControllerImpl implements SecurityController { } } }; + + /** + * A data class to hold specific Network properties received through the NetworkCallback. + */ + private static class NetworkProperties { + public String interfaceName; + public boolean validated; + + NetworkProperties(@NonNull String interfaceName, boolean validated) { + this.interfaceName = interfaceName; + this.validated = validated; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index 20fef927e8d1..e57c0e7f1044 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -210,7 +210,13 @@ public class ImageWallpaper extends WallpaperService { if (DEBUG) { Log.i(TAG, "onSurfaceDestroyed"); } - mSurfaceHolder = null; + mLongExecutor.execute(this::onSurfaceDestroyedSynchronized); + } + + private void onSurfaceDestroyedSynchronized() { + synchronized (mLock) { + mSurfaceHolder = null; + } } @Override @@ -241,7 +247,7 @@ public class ImageWallpaper extends WallpaperService { private void drawFrameInternal() { if (mSurfaceHolder == null) { - Log.e(TAG, "attempt to draw a frame without a valid surface"); + Log.i(TAG, "attempt to draw a frame without a valid surface"); return; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java new file mode 100644 index 000000000000..9dd337e43b6a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java @@ -0,0 +1,51 @@ +/* + * 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.accessibility.floatingmenu; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Notification; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class MenuNotificationFactoryTest extends SysuiTestCase { + private MenuNotificationFactory mMenuNotificationFactory; + + @Before + public void setUp() { + mMenuNotificationFactory = new MenuNotificationFactory(mContext); + } + + @Test + public void createHiddenNotification_hasUndoAndDeleteAction() { + Notification notification = mMenuNotificationFactory.createHiddenNotification(); + + assertThat(notification.contentIntent.getIntent().getAction()).isEqualTo( + MenuNotificationFactory.ACTION_UNDO); + assertThat(notification.deleteIntent.getIntent().getAction()).isEqualTo( + MenuNotificationFactory.ACTION_DELETE); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index be6f3ff8d3f9..68879a54cfe4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -21,15 +21,30 @@ import static android.view.View.VISIBLE; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; + +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE; +import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO; import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex; + import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.Notification; +import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -40,6 +55,8 @@ import android.os.Build; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -53,16 +70,21 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.test.filters.SmallTest; +import com.android.internal.messages.nano.SystemMessageProto; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; +import com.android.systemui.SysuiTestableContext; import com.android.systemui.util.settings.SecureSettings; +import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -98,6 +120,12 @@ public class MenuViewLayerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Spy + private SysuiTestableContext mSpyContext = getContext(); @Mock private IAccessibilityFloatingMenu mFloatingMenu; @@ -110,8 +138,12 @@ public class MenuViewLayerTest extends SysuiTestCase { @Mock private AccessibilityManager mStubAccessibilityManager; + private final NotificationManager mMockNotificationManager = mock(NotificationManager.class); + @Before public void setUp() throws Exception { + mSpyContext.addMockSystemService(Context.NOTIFICATION_SERVICE, mMockNotificationManager); + final Rect mDisplayBounds = new Rect(); mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH, DISPLAY_WINDOW_HEIGHT); @@ -119,31 +151,31 @@ public class MenuViewLayerTest extends SysuiTestCase { new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f)); doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics(); - mMenuViewLayer = new MenuViewLayer(mContext, mStubWindowManager, mStubAccessibilityManager, - mFloatingMenu, mSecureSettings); + mMenuViewLayer = new MenuViewLayer(mSpyContext, mStubWindowManager, + mStubAccessibilityManager, mFloatingMenu, mSecureSettings); mMenuView = (MenuView) mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW); mMenuAnimationController = mMenuView.getMenuAnimationController(); mLastAccessibilityButtonTargets = - Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.getStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT); mLastEnabledAccessibilityServices = - Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.getStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, UserHandle.USER_CURRENT); mMenuViewLayer.onAttachedToWindow(); - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT); - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "", UserHandle.USER_CURRENT); } @After public void tearDown() throws Exception { - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, mLastAccessibilityButtonTargets, UserHandle.USER_CURRENT); - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, mLastEnabledAccessibilityServices, UserHandle.USER_CURRENT); @@ -188,7 +220,7 @@ public class MenuViewLayerTest extends SysuiTestCase { setupEnabledAccessibilityServiceList(); mMenuViewLayer.mDismissMenuAction.run(); - final String value = Settings.Secure.getString(mContext.getContentResolver(), + final String value = Settings.Secure.getString(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); assertThat(value).isEqualTo(""); @@ -203,7 +235,7 @@ public class MenuViewLayerTest extends SysuiTestCase { AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY)).thenReturn(stubShortcutTargets); mMenuViewLayer.mDismissMenuAction.run(); - final String value = Settings.Secure.getString(mContext.getContentResolver(), + final String value = Settings.Secure.getString(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); assertThat(value).isEqualTo(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString()); @@ -278,9 +310,60 @@ public class MenuViewLayerTest extends SysuiTestCase { assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x); assertThat(mMenuView.getTranslationY()).isEqualTo(beforePosition.y); } + @Test + @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE) + public void onReleasedInTarget_hideMenuAndShowNotificationWithExpectedActions() { + dragMenuThenReleasedInTarget(); + + verify(mMockNotificationManager).notify( + eq(SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN), + any(Notification.class)); + ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass( + IntentFilter.class); + verify(mSpyContext).registerReceiver( + any(BroadcastReceiver.class), + intentFilterCaptor.capture(), + anyInt()); + assertThat(intentFilterCaptor.getValue().matchAction(ACTION_UNDO)).isTrue(); + assertThat(intentFilterCaptor.getValue().matchAction(ACTION_DELETE)).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE) + public void receiveActionUndo_dismissNotificationAndMenuVisible() { + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + dragMenuThenReleasedInTarget(); + + verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(), + any(IntentFilter.class), anyInt()); + broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_UNDO)); + + verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue()); + verify(mMockNotificationManager).cancel( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN); + assertThat(mMenuView.getVisibility()).isEqualTo(VISIBLE); + } + + @Test + @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE) + public void receiveActionDelete_dismissNotificationAndHideMenu() { + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + dragMenuThenReleasedInTarget(); + + verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(), + any(IntentFilter.class), anyInt()); + broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_DELETE)); + + verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue()); + verify(mMockNotificationManager).cancel( + SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN); + verify(mFloatingMenu).hide(); + } private void setupEnabledAccessibilityServiceList() { - Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.putString(mSpyContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString()); @@ -344,6 +427,12 @@ public class MenuViewLayerTest extends SysuiTestCase { springAnimation.skipToEnd(); springAnimation.doAnimationFrame(500); }); + } + private void dragMenuThenReleasedInTarget() { + MagnetizedObject.MagnetListener magnetListener = + mMenuViewLayer.getDragToInteractAnimationController().getMagnetListener(); + magnetListener.onReleasedInTarget( + new MagnetizedObject.MagneticTarget(mock(View.class), 200)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt index edd781dec3a0..2d9d5ed2b5e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt @@ -20,14 +20,16 @@ import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.util.mockito.mock +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds -import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -37,23 +39,21 @@ import org.junit.runners.JUnit4 @SmallTest @RunWith(JUnit4::class) class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { - private lateinit var underTest: KeyguardTransitionAnimationFlow.SharedFlowBuilder - private lateinit var repository: FakeKeyguardTransitionRepository - private lateinit var testScope: TestScope + val kosmos = testKosmos() + val testScope = kosmos.testScope + val animationFlow = kosmos.keyguardTransitionAnimationFlow + val repository = kosmos.fakeKeyguardTransitionRepository + + private lateinit var underTest: KeyguardTransitionAnimationFlow.FlowBuilder @Before fun setUp() { - testScope = TestScope() - repository = FakeKeyguardTransitionRepository() underTest = - KeyguardTransitionAnimationFlow( - testScope.backgroundScope, - mock(), - ) - .setup( - duration = 1000.milliseconds, - stepFlow = repository.transitions, - ) + animationFlow.setup( + duration = 1000.milliseconds, + from = KeyguardState.GONE, + to = KeyguardState.DREAMING, + ) } @Test(expected = IllegalArgumentException::class) @@ -83,6 +83,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onFinish = { 10f }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(1f, TransitionState.FINISHED), validateStep = false) assertThat(animationValues()).isEqualTo(10f) } @@ -97,6 +99,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onCancel = { 100f }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED)) assertThat(animationValues()).isEqualTo(100f) } @@ -111,6 +115,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onStep = { it }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) assertThat(animationValues()).isEqualTo(0f) @@ -137,6 +143,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { onStep = { it }, ) var animationValues = collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0f)) repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING)) @@ -157,17 +165,56 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { duration = 1000.milliseconds, onStep = { it * 2 }, ) - var animationValues = collectLastValue(flow) + val animationValues by collectLastValue(flow) + runCurrent() + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) - assertFloat(animationValues(), 0f) + assertFloat(animationValues, 0f) repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) - assertFloat(animationValues(), 0.6f) + assertFloat(animationValues, 0.6f) repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING)) - assertFloat(animationValues(), 1.2f) + assertFloat(animationValues, 1.2f) repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING)) - assertFloat(animationValues(), 1.6f) + assertFloat(animationValues, 1.6f) repository.sendTransitionStep(step(1f, TransitionState.RUNNING)) - assertFloat(animationValues(), 2f) + assertFloat(animationValues, 2f) + } + + @Test + fun sameFloatValueWithTheSameTransitionStateDoesNotEmitTwice() = + testScope.runTest { + val flow = + underTest.sharedFlow( + duration = 1000.milliseconds, + onStep = { it }, + ) + val values by collectValues(flow) + runCurrent() + + repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) + repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) + + assertThat(values.size).isEqualTo(1) + assertThat(values[0]).isEqualTo(0.3f) + } + + @Test + fun sameFloatValueWithADifferentTransitionStateDoesEmitTwice() = + testScope.runTest { + val flow = + underTest.sharedFlow( + duration = 1000.milliseconds, + onStep = { it }, + ) + val values by collectValues(flow) + runCurrent() + + repository.sendTransitionStep(step(0.3f, TransitionState.STARTED)) + repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING)) + + assertThat(values.size).isEqualTo(2) + assertThat(values[0]).isEqualTo(0.3f) + assertThat(values[0]).isEqualTo(0.3f) } private fun assertFloat(actual: Float?, expected: Float) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt index d959872fa80c..87391cce9136 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.testKosmos import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -50,6 +51,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { fun transitionToAlternateBouncer_scrimAlphaUpdate() = testScope.runTest { val scrimAlphas by collectValues(underTest.scrimAlpha) + runCurrent() transitionRepository.sendTransitionSteps( listOf( @@ -69,17 +71,17 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { fun transitionFromAlternateBouncer_scrimAlphaUpdate() = testScope.runTest { val scrimAlphas by collectValues(underTest.scrimAlpha) + runCurrent() transitionRepository.sendTransitionSteps( listOf( - stepToAlternateBouncer(0f, TransitionState.STARTED), - stepToAlternateBouncer(.4f), - stepToAlternateBouncer(.6f), - stepToAlternateBouncer(1f), + stepFromAlternateBouncer(0f, TransitionState.STARTED), + stepFromAlternateBouncer(.4f), + stepFromAlternateBouncer(.6f), + stepFromAlternateBouncer(1f), ), testScope, ) - assertThat(scrimAlphas.size).isEqualTo(4) scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt index af8d8a8978b5..795e68ddf335 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -63,6 +64,7 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // fade in repository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt index daafe12514ba..75994da6c934 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt @@ -39,6 +39,7 @@ import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -78,6 +79,8 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() + shadeRepository.setLockscreenShadeExpansion(1f) whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true) @@ -101,6 +104,8 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimAlpha_runDimissFromKeyguard_shadeNotExpanded() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() + shadeRepository.setLockscreenShadeExpansion(0f) whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true) @@ -123,6 +128,7 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimBehindAlpha_leaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) @@ -146,6 +152,8 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { fun scrimBehindAlpha_doNotLeaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) + runCurrent() + keyguardTransitionRepository.sendTransitionSteps( listOf( step(0f, TransitionState.STARTED), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt index dd542d482745..471029b17873 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt @@ -20,18 +20,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow -import com.android.systemui.util.mockito.mock +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -39,29 +36,10 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class DozingToLockscreenTransitionViewModelTest : SysuiTestCase() { - private lateinit var testScope: TestScope - private lateinit var underTest: DozingToLockscreenTransitionViewModel - private lateinit var repository: FakeKeyguardTransitionRepository - - @Before - fun setUp() { - testScope = TestScope() - repository = FakeKeyguardTransitionRepository() - underTest = - DozingToLockscreenTransitionViewModel( - interactor = - KeyguardTransitionInteractorFactory.create( - scope = testScope.backgroundScope, - repository = repository, - ) - .keyguardTransitionInteractor, - animationFlow = - KeyguardTransitionAnimationFlow( - scope = testScope.backgroundScope, - logger = mock() - ), - ) - } + val kosmos = testKosmos() + val testScope = kosmos.testScope + val repository = kosmos.fakeKeyguardTransitionRepository + val underTest = kosmos.dozingToLockscreenTransitionViewModel @Test fun deviceEntryParentViewShows() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt index a105008f3f37..1c9c942eafc6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.testKosmos import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -52,6 +53,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { val pixels = -100f val enterFromTopTranslationY by collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt())) + runCurrent() // The animation should only start > .4f way through repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -72,6 +74,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fun enterFromTopAnimationAlpha() = testScope.runTest { val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha) + runCurrent() // The animation should only start > .4f way through repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -92,6 +95,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { testScope.runTest { val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 0f repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -113,6 +117,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -137,6 +142,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsRearFps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end repository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -161,6 +167,7 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end repository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt index 5e6231734d32..1912987cc447 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -51,6 +52,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { testScope.runTest { val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 0f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -72,6 +74,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // immediately 1f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -96,6 +99,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsRearFps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // no updates keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -120,6 +124,7 @@ class OccludedToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // no updates keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt index 9729022ca890..c55c27c3b516 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -54,6 +55,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() val deviceEntryBackgroundViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 0f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -75,6 +77,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -92,6 +95,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsRearFps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() // animation doesn't start until the end keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) @@ -116,6 +120,7 @@ class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() { fingerprintPropertyRepository.supportsUdfps() biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha) + runCurrent() keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) assertThat(deviceEntryParentViewAlpha).isNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt index 2c6436e07b91..0796af065790 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -71,6 +72,7 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() { testScope.runTest { fingerprintPropertyRepository.supportsUdfps() val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha) + runCurrent() // immediately 1f keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 5569ca9520e9..b7a9ea751438 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.os.PowerManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils @@ -43,6 +44,8 @@ import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -52,6 +55,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Mock private lateinit var communalViewModel: CommunalViewModel @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock private lateinit var shadeInteractor: ShadeInteractor + @Mock private lateinit var powerManager: PowerManager private lateinit var containerView: View private lateinit var testableLooper: TestableLooper @@ -76,7 +80,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { communalInteractor, communalViewModel, keyguardTransitionInteractor, - shadeInteractor + shadeInteractor, + powerManager ) testableLooper = TestableLooper.get(this) @@ -90,14 +95,14 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun isEnabled_interactorEnabled_returnsTrue() { + fun isEnabled_interactorEnabled_interceptsTouches() { communalRepository.setIsCommunalEnabled(true) assertThat(underTest.isEnabled()).isTrue() } @Test - fun isEnabled_interactorDisabled_returnsFalse() { + fun isEnabled_interactorDisabled_doesNotIntercept() { communalRepository.setIsCommunalEnabled(false) assertThat(underTest.isEnabled()).isFalse() @@ -120,7 +125,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_touchInsideGestureRegion_returnsTrue() { + fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -131,7 +136,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_subsequentTouchesAfterGestureStart_returnsTrue() { + fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -146,7 +151,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_communalOpen_returnsTrue() { + fun onTouchEvent_communalOpen_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -155,10 +160,12 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // Touch events are intercepted. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue() + // User activity sent to PowerManager. + verify(powerManager).userActivity(any(), any(), any()) } @Test - fun onTouchEvent_communalAndBouncerShowing_returnsFalse() { + fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -170,10 +177,12 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // Touch events are not intercepted. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + // User activity is not sent to PowerManager. + verify(powerManager, times(0)).userActivity(any(), any(), any()) } @Test - fun onTouchEvent_communalAndShadeShowing_returnsFalse() { + fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) 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 a20658197a8d..b2394820b2af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -346,6 +346,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected ActiveNotificationsInteractor mActiveNotificationsInteractor; @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm; @Mock private NaturalScrollingSettingObserver mNaturalScrollingSettingObserver; + @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper; protected final int mMaxUdfpsBurnInOffsetY = 5; protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); @@ -426,7 +427,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mContext, new ResourcesSplitShadeStateController(), mKeyguardInteractor, - deviceEntryUdfpsInteractor + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper ), mShadeRepository ) @@ -812,7 +814,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mActiveNotificationsInteractor, mJavaAdapter, mCastController, - new ResourcesSplitShadeStateController() + new ResourcesSplitShadeStateController(), + () -> mLargeScreenHeaderHelper ); } 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 9d8b21464585..437d00ac8723 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -143,6 +143,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock private UserTracker mUserTracker; @Mock private SceneContainerFlags mSceneContainerFlags; + @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper; @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener; @@ -261,7 +262,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mContext, new ResourcesSplitShadeStateController(), keyguardInteractor, - deviceEntryUdfpsInteractor), + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper), shadeRepository ) ); 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 88a47eb81bde..ea3caa380cf2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -52,7 +53,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.RETURNS_DEEP_STUBS import org.mockito.Mockito.any @@ -74,15 +74,16 @@ import org.mockito.MockitoAnnotations @SmallTest class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { - @Mock lateinit var view: NotificationsQuickSettingsContainer - @Mock lateinit var navigationModeController: NavigationModeController - @Mock lateinit var overviewProxyService: OverviewProxyService - @Mock lateinit var shadeHeaderController: ShadeHeaderController - @Mock lateinit var shadeInteractor: ShadeInteractor - @Mock lateinit var fragmentService: FragmentService - @Mock lateinit var fragmentHostManager: FragmentHostManager - @Mock - lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController + private val view = mock<NotificationsQuickSettingsContainer>() + private val navigationModeController = mock<NavigationModeController>() + private val overviewProxyService = mock<OverviewProxyService>() + private val shadeHeaderController = mock<ShadeHeaderController>() + private val shadeInteractor = mock<ShadeInteractor>() + private val fragmentService = mock<FragmentService>() + private val fragmentHostManager = mock<FragmentHostManager>() + private val notificationStackScrollLayoutController = + mock<NotificationStackScrollLayoutController>() + private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>() @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener> @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener> @@ -123,7 +124,8 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN) @@ -480,7 +482,8 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) controller.updateConstraints() 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 1f37ca09dc26..c1bc303f26ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -53,7 +54,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.RETURNS_DEEP_STUBS import org.mockito.Mockito.any @@ -71,15 +71,16 @@ import org.mockito.MockitoAnnotations @SmallTest class NotificationsQSContainerControllerTest : SysuiTestCase() { - @Mock lateinit var view: NotificationsQuickSettingsContainer - @Mock lateinit var navigationModeController: NavigationModeController - @Mock lateinit var overviewProxyService: OverviewProxyService - @Mock lateinit var shadeHeaderController: ShadeHeaderController - @Mock lateinit var shadeInteractor: ShadeInteractor - @Mock lateinit var fragmentService: FragmentService - @Mock lateinit var fragmentHostManager: FragmentHostManager - @Mock - lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController + private val view = mock<NotificationsQuickSettingsContainer>() + private val navigationModeController = mock<NavigationModeController>() + private val overviewProxyService = mock<OverviewProxyService>() + private val shadeHeaderController = mock<ShadeHeaderController>() + private val shadeInteractor = mock<ShadeInteractor>() + private val fragmentService = mock<FragmentService>() + private val fragmentHostManager = mock<FragmentHostManager>() + private val notificationStackScrollLayoutController = + mock<NotificationStackScrollLayoutController>() + private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>() @Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener> @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener> @@ -122,7 +123,8 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN) @@ -463,7 +465,8 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { delayableExecutor, featureFlags, notificationStackScrollLayoutController, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) controller.updateConstraints() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index 727a6c3d1adc..39051eba3ad9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -176,6 +176,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { @Mock protected CastController mCastController; @Mock protected UserSwitcherInteractor mUserSwitcherInteractor; @Mock protected SelectedUserInteractor mSelectedUserInteractor; + @Mock protected LargeScreenHeaderHelper mLargeScreenHeaderHelper; protected FakeDisableFlagsRepository mDisableFlagsRepository = new FakeDisableFlagsRepository(); @@ -299,7 +300,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mContext, splitShadeStateController, keyguardInteractor, - deviceEntryUdfpsInteractor), + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper), mShadeRepository ) ); @@ -384,7 +386,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mActiveNotificationsInteractor, new JavaAdapter(mTestScope.getBackgroundScope()), mCastController, - splitShadeStateController + splitShadeStateController, + () -> mLargeScreenHeaderHelper ); mQsController.init(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index ee27c5c9ba0d..64fd80d72d3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginManager import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq +import java.util.function.BiConsumer import junit.framework.Assert.assertEquals import junit.framework.Assert.fail import kotlinx.coroutines.CoroutineDispatcher @@ -100,10 +101,7 @@ class ClockRegistryTest : SysuiTestCase() { override fun toString() = "Manager[$tag]" override fun getPackage(): String = mComponentName.getPackageName() override fun getComponentName(): ComponentName = mComponentName - - private var isDebug: Boolean = false - override fun getIsDebug(): Boolean = isDebug - override fun setIsDebug(value: Boolean) { isDebug = value } + override fun setLogFunc(func: BiConsumer<String, String>) { } override fun loadPlugin() { if (!mIsLoaded) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java index bc50c25a77a0..3defee9e6eb5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java @@ -112,7 +112,7 @@ public class PluginInstanceTest extends SysuiTestCase { mPluginInstance = mPluginInstanceFactory.create( mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME, TestPlugin.class, mPluginListener); - mPluginInstance.setIsDebug(true); + mPluginInstance.setLogFunc((tag, msg) -> Log.d((String) tag, (String) msg)); mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index f25ce0aa5278..aed616349eb3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -27,7 +27,7 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -44,6 +44,7 @@ import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.shade.LargeScreenHeaderHelper import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl @@ -65,7 +66,6 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify @@ -83,9 +83,10 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor private lateinit var fromPrimaryBouncerTransitionInteractor: FromPrimaryBouncerTransitionInteractor - @Mock lateinit var interactionJankMonitor: InteractionJankMonitor - @Mock lateinit var mockDarkAnimator: ObjectAnimator - @Mock lateinit var deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor + private val interactionJankMonitor = mock<InteractionJankMonitor>() + private val mockDarkAnimator = mock<ObjectAnimator>() + private val deviceEntryUdfpsInteractor = mock<DeviceEntryUdfpsInteractor>() + private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>() private lateinit var controller: StatusBarStateControllerImpl private lateinit var uiEventLogger: UiEventLoggerFake @@ -189,6 +190,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { ResourcesSplitShadeStateController(), keyguardInteractor, deviceEntryUdfpsInteractor, + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ), shadeRepository, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 3556703a2fa8..4dc4798caa8c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -84,7 +84,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { LogBuffer logBuffer = FakeLogBuffer.Factory.Companion.create(); mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(logBuffer); when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); mClockPosition = new KeyguardClockPositionAlgorithm.Result(); } @@ -297,7 +297,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { .thenReturn(100); when(mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)) .thenReturn(70); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); givenLockScreen(); mIsSplitShade = true; // WHEN the position algorithm is run @@ -589,7 +589,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private void setSplitShadeTopMargin(int value) { when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)) .thenReturn(value); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); } private void givenHighestBurnInOffset() { @@ -603,7 +603,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private void givenMaxBurnInOffset(int offset) { when(mResources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y_clock)) .thenReturn(offset); - mClockPositionAlgorithm.loadDimens(mResources); + mClockPositionAlgorithm.loadDimens(mContext, mResources); } private void givenAOD() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt index a906a8953e02..02e6fd5a9d6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt @@ -31,7 +31,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN import android.telephony.satellite.SatelliteManager.SatelliteException -import android.telephony.satellite.SatelliteStateCallback +import android.telephony.satellite.SatelliteModemStateCallback import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -106,7 +106,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { val latest by collectLastValue(underTest.connectionState) runCurrent() val callback = - withArgCaptor<SatelliteStateCallback> { + withArgCaptor<SatelliteModemStateCallback> { verify(satelliteManager).registerForSatelliteModemStateChanged(any(), capture()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 1dab84eb6e6a..cb6ce68aaf80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -214,7 +215,8 @@ public class SecurityControllerTest extends SysuiTestCase { public void testNetworkRequest() { verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat( (NetworkRequest request) -> - request.equals(new NetworkRequest.Builder().clearCapabilities().build()) + request.equals(new NetworkRequest.Builder() + .clearCapabilities().addTransportType(TRANSPORT_VPN).build()) ), any(NetworkCallback.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt index 6714c94b017c..fb5375a1ab83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt @@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.SysuiTestCase @@ -121,6 +122,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { ) utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_SWITCH_USER_ON_BG) spyContext = spy(context) keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.featureFlags) keyguardRepository = keyguardReply.repository @@ -172,6 +174,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) underTest.onRecordSelected(UserRecord(info = userInfos[1]), dialogShower) + runCurrent() verify(uiEventLogger, times(1)) .log(MultiUserActionsEvent.SWITCH_TO_USER_FROM_USER_SWITCHER) @@ -191,6 +194,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) underTest.onRecordSelected(UserRecord(info = userInfos.last())) + runCurrent() verify(uiEventLogger, times(1)) .log(MultiUserActionsEvent.SWITCH_TO_GUEST_FROM_USER_SWITCHER) @@ -218,6 +222,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) underTest.onRecordSelected(UserRecord(info = userInfos.last())) + runCurrent() verify(uiEventLogger, times(1)) .log(MultiUserActionsEvent.SWITCH_TO_RESTRICTED_USER_FROM_USER_SWITCHER) 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 814ea19a7dcb..b7529a82dd3d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -126,6 +126,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.LargeScreenHeaderHelper; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.ShadeController; @@ -349,6 +350,8 @@ public class BubblesTest extends SysuiTestCase { private Display mDefaultDisplay; @Mock private SceneContainerFlags mSceneContainerFlags; + @Mock + private LargeScreenHeaderHelper mLargeScreenHeaderHelper; private final SceneTestUtils mUtils = new SceneTestUtils(this); private final TestScope mTestScope = mUtils.getTestScope(); @@ -490,7 +493,8 @@ public class BubblesTest extends SysuiTestCase { mContext, splitShadeStateController, keyguardInteractor, - deviceEntryUdfpsInteractor), + deviceEntryUdfpsInteractor, + () -> mLargeScreenHeaderHelper), shadeRepository ) ); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt index eb287ee522c0..95ff889177b8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt @@ -35,7 +35,8 @@ object CommunalInteractorFactory { @JvmStatic fun create( testScope: TestScope = TestScope(), - communalRepository: FakeCommunalRepository = FakeCommunalRepository(), + communalRepository: FakeCommunalRepository = + FakeCommunalRepository(testScope.backgroundScope), widgetRepository: FakeCommunalWidgetRepository = FakeCommunalWidgetRepository(testScope.backgroundScope), mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(), @@ -51,6 +52,7 @@ object CommunalInteractorFactory { communalRepository = communalRepository, ) return WithDependencies( + testScope, communalRepository, widgetRepository, mediaRepository, @@ -74,6 +76,7 @@ object CommunalInteractorFactory { } data class WithDependencies( + val testScope: TestScope, val communalRepository: FakeCommunalRepository, val widgetRepository: FakeCommunalWidgetRepository, val mediaRepository: FakeCommunalMediaRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index a94ca291298d..0c1dbfebfb34 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -147,7 +147,6 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio ) } } - _transitions.emit(step) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt index 8d6529a114b8..dad1887cbd85 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui import com.android.keyguard.logging.keyguardTransitionAnimationLogger +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope @@ -27,6 +28,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.keyguardTransitionAnimationFlow by Fixture { KeyguardTransitionAnimationFlow( scope = applicationCoroutineScope, + transitionInteractor = keyguardTransitionInteractor, logger = keyguardTransitionAnimationLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt index d9c6e4f1f605..3ed9392bab2a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.alternateBouncerToAodTransitionViewModel by Fixture { AlternateBouncerToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt index e4821b04fcef..c909dd6ffdd5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.alternateBouncerToGoneTransitionViewModel by Fixture { AlternateBouncerToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, bouncerToGoneFlows = bouncerToGoneFlows, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt index 9f0466dda51e..b4f1218617a2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -28,7 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.alternateBouncerViewModel by Fixture { AlternateBouncerViewModel( statusBarKeyguardViewManager = statusBarKeyguardViewManager, - transitionInteractor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt index 44e542660971..b6f278c1b466 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToGoneTransitionViewModel by Fixture { AodToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt index b5a5f039200f..733340c67e55 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToLockscreenTransitionViewModel by Fixture { AodToLockscreenTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt index 27ad0f0f01e3..8d066fc05996 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToOccludedTransitionViewModel by Fixture { AodToOccludedTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt index 6ffcc9a03b05..c71c1c3ea5f0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.flags.featureFlagsClassic -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -31,7 +30,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.bouncerToGoneFlows by Fixture { BouncerToGoneFlows( - interactor = keyguardTransitionInteractor, statusBarStateController = sysuiStatusBarStateController, primaryBouncerInteractor = primaryBouncerInteractor, keyguardDismissActionInteractor = mock(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt new file mode 100644 index 000000000000..400a0d87f041 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.dozingToLockscreenTransitionViewModel by Fixture { + DozingToLockscreenTransitionViewModel( + animationFlow = keyguardTransitionAnimationFlow, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt index 00ece1482236..19e4241c62fa 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi var Kosmos.goneToAodTransitionViewModel by Fixture { GoneToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt index 073b34bcf277..b267a962a1ff 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.goneToDreamingTransitionViewModel by Fixture { GoneToDreamingTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt index 7865f71ead83..07b4cd4f3251 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToAodTransitionViewModel by Fixture { LockscreenToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, shadeDependentFlows = shadeDependentFlows, animationFlow = keyguardTransitionAnimationFlow, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt index b9f4b71d24d6..56d5ff6e30eb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToDreamingTransitionViewModel by Fixture { LockscreenToDreamingTransitionViewModel( - interactor = keyguardTransitionInteractor, shadeDependentFlows = shadeDependentFlows, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt index 475aa2de3f9b..1b2337fedf6a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToGoneTransitionViewModel by Fixture { LockscreenToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt index 8541a4fe7096..9953d39e9a49 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.common.ui.domain.interactor.configurationInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToOccludedTransitionViewModel by Fixture { LockscreenToOccludedTransitionViewModel( - interactor = keyguardTransitionInteractor, shadeDependentFlows = shadeDependentFlows, configurationInteractor = configurationInteractor, animationFlow = keyguardTransitionAnimationFlow, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt index 65c47fc9c2c7..f094f22c9f50 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -26,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.lockscreenToPrimaryBouncerTransitionViewModel by Fixture { LockscreenToPrimaryBouncerTransitionViewModel( - interactor = keyguardTransitionInteractor, shadeDependentFlows = shadeDependentFlows, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt index ddde5498d544..b7867b6cabde 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.occludedToAodTransitionViewModel by Fixture { OccludedToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt index 93ecb7968ee2..e6651a44236f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -28,7 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi var Kosmos.occludedToLockscreenTransitionViewModel by Fixture { OccludedToLockscreenTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, configurationInteractor = configurationInteractor, animationFlow = keyguardTransitionAnimationFlow, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt index a7f29d637281..8d887309f576 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.primaryBouncerToAodTransitionViewModel by Fixture { PrimaryBouncerToAodTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt index ace6ae3e3eec..ab28d0d670ef 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.flags.featureFlagsClassic -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -30,7 +29,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.primaryBouncerToGoneTransitionViewModel by Fixture { PrimaryBouncerToGoneTransitionViewModel( - interactor = keyguardTransitionInteractor, statusBarStateController = sysuiStatusBarStateController, primaryBouncerInteractor = primaryBouncerInteractor, keyguardDismissActionInteractor = mock(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt index 3bbabf713b91..85662512a5ee 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.primaryBouncerToLockscreenTransitionViewModel by Fixture { PrimaryBouncerToLockscreenTransitionViewModel( - interactor = keyguardTransitionInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt new file mode 100644 index 000000000000..d3e757469a99 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/LargeScreenHeaderHelperKosmos.kt @@ -0,0 +1,24 @@ +/* + * 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.shade + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.util.mockito.mock + +var Kosmos.largeScreenHeaderHelper by Fixture { mockLargeScreenHeaderHelper } +val Kosmos.mockLargeScreenHeaderHelper by Fixture { mock<LargeScreenHeaderHelper>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt index 13d577bde711..8909d751227a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.common.ui.data.repository.configurationRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.shade.largeScreenHeaderHelper import com.android.systemui.statusbar.policy.splitShadeStateController val Kosmos.sharedNotificationContainerInteractor by @@ -31,5 +32,6 @@ val Kosmos.sharedNotificationContainerInteractor by splitShadeStateController = splitShadeStateController, keyguardInteractor = keyguardInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt index 021e7dff9120..ac90a45450d0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt @@ -77,6 +77,8 @@ class FakeSecurityController( override fun isVpnBranded(): Boolean = fakeState.isVpnBranded + override fun isVpnValidated(): Boolean = fakeState.isVpnValidated + override fun getPrimaryVpnName(): String? = fakeState.primaryVpnName override fun getWorkProfileVpnName(): String? = fakeState.workProfileVpnName @@ -110,6 +112,7 @@ class FakeSecurityController( var isVpnEnabled: Boolean = false, var isVpnRestricted: Boolean = false, var isVpnBranded: Boolean = false, + var isVpnValidated: Boolean = false, var primaryVpnName: String? = null, var workProfileVpnName: String? = null, var hasCACertInCurrentUser: Boolean = false, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java index 76199e3168da..791165d97795 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java @@ -109,6 +109,11 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa } @Override + public boolean isVpnValidated() { + return false; + } + + @Override public String getPrimaryVpnName() { return null; } diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp index e52cefb2d7e4..81fd8ce12f05 100644 --- a/packages/SystemUI/unfold/Android.bp +++ b/packages/SystemUI/unfold/Android.bp @@ -39,7 +39,4 @@ android_library { sdk_version: "current", min_sdk_version: "current", plugins: ["dagger2-compiler"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SystemUI/unfold/lint-baseline.xml b/packages/SystemUI/unfold/lint-baseline.xml deleted file mode 100644 index 449ed2e60853..000000000000 --- a/packages/SystemUI/unfold/lint-baseline.xml +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> -</issues> diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index b403a7fe8f12..7f542d130cf7 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -408,5 +408,9 @@ message SystemMessage { // Notify the user about external display events related to screenshot. // Package: com.android.systemui NOTE_GLOBAL_SCREENSHOT_EXTERNAL_DISPLAY = 1008; + + // Notify the user that accessibility floating menu is hidden. + // Package: com.android.systemui + NOTE_A11Y_FLOATING_MENU_HIDDEN = 1009; } } diff --git a/services/Android.bp b/services/Android.bp index 0b484f473d36..7e8333c7ba5f 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -148,9 +148,6 @@ filegroup { java_library { name: "Slogf", srcs: ["core/java/com/android/server/utils/Slogf.java"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // merge all required services into one jar diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp index a3546716d5ca..69cc68a55108 100644 --- a/services/accessibility/Android.bp +++ b/services/accessibility/Android.bp @@ -22,6 +22,7 @@ java_library_static { lint: { error_checks: ["MissingPermissionAnnotation"], baseline_filename: "lint-baseline.xml", + }, srcs: [ ":services.accessibility-sources", @@ -50,9 +51,6 @@ java_library_static { libs: [ "androidx.annotation_annotation", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } aconfig_declarations { diff --git a/services/accessibility/lint-baseline.xml b/services/accessibility/lint-baseline.xml index 6bec8cf5f018..b808219ef9c6 100644 --- a/services/accessibility/lint-baseline.xml +++ b/services/accessibility/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -23,4 +23,4 @@ column="9"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig index d695d36db0ea..549fa36597b7 100644 --- a/services/backup/flags.aconfig +++ b/services/backup/flags.aconfig @@ -7,4 +7,12 @@ flag { "restore for apps that have been launched." bug: "308401499" is_fixed_read_only: true +} + +flag { + name: "enable_max_size_writes_to_pipes" + namespace: "onboarding" + description: "Enables the write buffer to pipes to be of maximum size." + bug: "265976737" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index 6aed9aa15860..cca166b0939c 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -40,8 +40,8 @@ import android.util.Slog; import com.android.server.EventLogTags; import com.android.server.backup.BackupAgentTimeoutParameters; -import com.android.server.backup.BackupAndRestoreFeatureFlags; import com.android.server.backup.BackupRestoreTask; +import com.android.server.backup.Flags; import com.android.server.backup.FullBackupJob; import com.android.server.backup.OperationStorage; import com.android.server.backup.OperationStorage.OpState; @@ -390,8 +390,11 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // Set up to send data to the transport final int N = mPackages.size(); - final int chunkSizeInBytes = - BackupAndRestoreFeatureFlags.getFullBackupWriteToTransportBufferSizeBytes(); + int chunkSizeInBytes = 8 * 1024; // 8KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + chunkSizeInBytes = 64 * 1024; // 64KB + } final byte[] buffer = new byte[chunkSizeInBytes]; for (int i = 0; i < N; i++) { mBackupRunner = null; diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index ff72476d4bf1..2c9eb51972af 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -29,7 +29,6 @@ import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; import android.app.backup.BackupAgent; import android.app.backup.BackupAnnotations; -import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IFullBackupRestoreObserver; @@ -51,6 +50,7 @@ import com.android.server.LocalServices; import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.FileMetadata; +import com.android.server.backup.Flags; import com.android.server.backup.KeyValueAdbRestoreEngine; import com.android.server.backup.OperationStorage; import com.android.server.backup.OperationStorage.OpType; @@ -157,13 +157,19 @@ public class FullRestoreEngine extends RestoreEngine { mMonitor = monitor; mOnlyPackage = onlyPackage; mAllowApks = allowApks; - mBuffer = new byte[32 * 1024]; mAgentTimeoutParameters = Objects.requireNonNull( backupManagerService.getAgentTimeoutParameters(), "Timeout parameters cannot be null"); mIsAdbRestore = isAdbRestore; mUserId = backupManagerService.getUserId(); mBackupEligibilityRules = backupEligibilityRules; + + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + mBuffer = new byte[64 * 1024]; // 64KB + } else { + mBuffer = new byte[32 * 1024]; + } } @VisibleForTesting diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 316a16d9934c..2fbc3cd24d65 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -968,7 +968,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { throws Exception { Set<String> excludedKeysForPackage = getExcludedKeysForPackage(packageName); - byte[] buffer = new byte[8192]; // will grow when needed + int bufferSize = 8192; // 8KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + bufferSize = 64 * 1024; // 64KB + } + byte[] buffer = new byte[bufferSize]; // will grow when needed while (in.readNextHeader()) { final String key = in.getKey(); final int size = in.getDataSize(); @@ -1116,7 +1121,11 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { ParcelFileDescriptor tReadEnd = mTransportPipes[0]; ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; - int bufferSize = 32 * 1024; + int bufferSize = 32 * 1024; // 32KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + bufferSize = 64 * 1024; // 64KB + } byte[] buffer = new byte[bufferSize]; FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java index 1c0cd87cec6f..843354e719bb 100644 --- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java @@ -21,7 +21,7 @@ import static com.android.server.backup.BackupManagerService.TAG; import android.os.ParcelFileDescriptor; import android.util.Slog; -import com.android.server.backup.BackupAndRestoreFeatureFlags; +import com.android.server.backup.Flags; import java.io.DataInputStream; import java.io.EOFException; @@ -46,8 +46,11 @@ public class FullBackupUtils { // We do not take close() responsibility for the pipe FD FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); DataInputStream in = new DataInputStream(raw); - final int chunkSizeInBytes = - BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes(); + int chunkSizeInBytes = 32 * 1024; // 32KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + chunkSizeInBytes = 64 * 1024; // 64KB + } byte[] buffer = new byte[chunkSizeInBytes]; int chunkTotal; while ((chunkTotal = in.readInt()) > 0) { diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java index 0accb9fadd04..5a8533a2daee 100644 --- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java +++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java @@ -40,6 +40,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.backup.FileMetadata; +import com.android.server.backup.Flags; import com.android.server.backup.restore.RestoreDeleteObserver; import com.android.server.backup.restore.RestorePolicy; @@ -93,7 +94,12 @@ public class RestoreUtils { try (Session session = installer.openSession(sessionId)) { try (OutputStream apkStream = session.openWrite(info.packageName, 0, info.size)) { - byte[] buffer = new byte[32 * 1024]; + int bufferSize = 32 * 1024; // 32KB + if (Flags.enableMaxSizeWritesToPipes()) { + // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB + bufferSize = 64 * 1024; // 64KB + } + byte[] buffer = new byte[bufferSize]; long size = info.size; while (size > 0) { long toRead = (buffer.length < size) ? buffer.length : size; diff --git a/services/backup/lint-baseline.xml b/services/backup/lint-baseline.xml index 93c9390feb9c..46de2cdd7a47 100644 --- a/services/backup/lint-baseline.xml +++ b/services/backup/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -36,4 +36,4 @@ line="207"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java index 720687ef20cc..0e66fbc020a1 100644 --- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java @@ -23,12 +23,12 @@ import android.content.Context; import android.os.Build; import android.util.Slog; -import com.google.security.cryptauth.lib.securegcm.BadHandleException; -import com.google.security.cryptauth.lib.securegcm.CryptoException; -import com.google.security.cryptauth.lib.securegcm.D2DConnectionContextV1; -import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext; -import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext.Role; -import com.google.security.cryptauth.lib.securegcm.HandshakeException; +import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException; +import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException; +import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1; +import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext; +import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.Role; +import com.google.security.cryptauth.lib.securegcm.ukey2.HandshakeException; import libcore.io.IoUtils; import libcore.io.Streams; diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index 1f89e57b90d7..d1274d49a14d 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -245,8 +245,8 @@ class InputController { mInputManagerInternal.setPointerIconVisible(visible, displayId); } - void setPointerAcceleration(float pointerAcceleration, int displayId) { - mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId); + void setMousePointerAccelerationEnabled(boolean enabled, int displayId) { + mInputManagerInternal.setMousePointerAccelerationEnabled(enabled, displayId); } void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 58aa2c303345..44c3a8d7537f 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -1110,7 +1110,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub final long token = Binder.clearCallingIdentity(); try { mInputController.setShowPointerIcon(showPointer, displayId); - mInputController.setPointerAcceleration(1f, displayId); + mInputController.setMousePointerAccelerationEnabled(false, displayId); mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, displayId); // WM throws a SecurityException if the display is untrusted. diff --git a/services/companion/lint-baseline.xml b/services/companion/lint-baseline.xml index 03eae3901e51..020126f75ea0 100644 --- a/services/companion/lint-baseline.xml +++ b/services/companion/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -12,4 +12,4 @@ column="14"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/core/Android.bp b/services/core/Android.bp index a3fc3bf5ec72..a6ed498e93db 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -234,9 +234,6 @@ java_genrule { java_library { name: "services.core", static_libs: ["services.core.priorityboosted"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library_host { diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 9eb35fde50fb..eb6fdd72f2c3 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -101,6 +101,7 @@ import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.TelephonyPermissions; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.util.TelephonyUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; @@ -2679,6 +2680,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!checkNotifyPermission("notifyEmergencyNumberList()")) { return; } + if (Flags.enforceTelephonyFeatureMappingForPublicApis()) { + if (!mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEPHONY_CALLING)) { + // TelephonyManager.getEmergencyNumberList() throws an exception if + // FEATURE_TELEPHONY_CALLING is not defined. + return; + } + } synchronized (mRecords) { if (validatePhoneId(phoneId)) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 02f4485d5b40..a78bebd0e87b 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -5453,7 +5453,7 @@ public final class ActiveServices { // Force an immediate oomAdjUpdate, so the client app could be in the correct process state // before doing any service related transactions mAm.enqueueOomAdjTargetLocked(app); - mAm.updateOomAdjLocked(app, OOM_ADJ_REASON_START_SERVICE); + mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE); boolean created = false; try { diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 8ad60e6a0782..72e62c37106d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -243,7 +243,7 @@ final class ActivityManagerConstants extends ContentObserver { /** * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}. */ - private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite(); + private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false; /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java index e0a224629174..9fc0bf920969 100644 --- a/services/core/java/com/android/server/am/AnrHelper.java +++ b/services/core/java/com/android/server/am/AnrHelper.java @@ -63,6 +63,11 @@ class AnrHelper { private static final long CONSECUTIVE_ANR_TIME_MS = TimeUnit.MINUTES.toMillis(2); /** + * Time to wait before taking dumps for other processes to reduce load at boot time. + */ + private static final long SELF_ONLY_AFTER_BOOT_MS = TimeUnit.MINUTES.toMillis(10); + + /** * The keep alive time for the threads in the helper threadpool executor */ private static final int DEFAULT_THREAD_KEEP_ALIVE_SECOND = 10; @@ -231,7 +236,8 @@ class AnrHelper { // If there are many ANR at the same time, the latency may be larger. // If the latency is too large, the stack trace might not be meaningful. final long reportLatency = startTime - r.mTimestamp; - final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS; + final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS + || startTime < SELF_ONLY_AFTER_BOOT_MS; r.appNotResponding(onlyDumpSelf); final long endTime = SystemClock.uptimeMillis(); Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in " diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java index 9f31f375dafe..5f12ce1e4163 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java @@ -73,7 +73,8 @@ public class AmbientContextManagerService extends private static final Set<Integer> DEFAULT_EVENT_SET = Sets.newHashSet( AmbientContextEvent.EVENT_COUGH, AmbientContextEvent.EVENT_SNORE, - AmbientContextEvent.EVENT_BACK_DOUBLE_TAP); + AmbientContextEvent.EVENT_BACK_DOUBLE_TAP, + AmbientContextEvent.EVENT_HEART_RATE); /** Default value in absence of {@link DeviceConfig} override. */ private static final boolean DEFAULT_SERVICE_ENABLED = true; diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index dada72eb51d9..f80228afa52d 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1812,6 +1812,7 @@ public class AudioDeviceBroker { "msg: MSG_L_SET_BT_ACTIVE_DEVICE " + "received with null profile proxy: " + btInfo)).printLog(TAG)); + sendMsg(MSG_CHECK_MUTE_MUSIC, SENDMSG_REPLACE, 0 /*delay*/); return; } @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec = diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e05824a5f1ab..bf20ae3b516d 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -1992,7 +1992,7 @@ public class AudioDeviceInventory { // TODO: return; } else { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address) + "A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address) + " now available").printLog(TAG)); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 91d533c73b5d..4cbee2b89bb2 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -8835,6 +8835,8 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { oldIndex = getIndex(device); index = getValidIndex(index, hasModifyAudioSettings); + // for STREAM_SYSTEM_ENFORCED, do not sync aliased streams on the enforced index + int aliasIndex = index; if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) { index = mIndexMax; } @@ -8853,7 +8855,8 @@ public class AudioService extends IAudioService.Stub if (streamType != mStreamType && mStreamVolumeAlias[streamType] == mStreamType && (changed || !aliasStreamState.hasIndexForDevice(device))) { - final int scaledIndex = rescaleIndex(index, mStreamType, streamType); + final int scaledIndex = + rescaleIndex(aliasIndex, mStreamType, streamType); aliasStreamState.setIndex(scaledIndex, device, caller, hasModifyAudioSettings); if (isCurrentDevice) { @@ -9375,6 +9378,14 @@ public class AudioService extends IAudioService.Stub if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) { return; } + + // Persisting STREAM_SYSTEM_ENFORCED index is not needed as its alias (STREAM_RING) + // is persisted. This can also be problematic when the enforcement is active as it will + // override current SYSTEM_RING persisted value given they share the same settings name + // (due to aliasing). + if (streamState.mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) { + return; + } if (streamState.hasValidSettingsName()) { mSettings.putSystemIntForUser(mContentResolver, streamState.getSettingNameForDevice(device), diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index a818c30c2e28..f51043dc1cdc 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -634,16 +634,17 @@ public class BtHelper { return; } List<BluetoothDevice> activeDevices = adapter.getActiveDevices(profile); - if (activeDevices.isEmpty() || activeDevices.get(0) == null) { - return; + BluetoothProfileConnectionInfo bpci = new BluetoothProfileConnectionInfo(profile); + for (BluetoothDevice device : activeDevices) { + if (device == null) { + continue; + } + AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData( + device, null, bpci, "mBluetoothProfileServiceListener"); + AudioDeviceBroker.BtDeviceInfo info = mDeviceBroker.createBtDeviceInfo( + data, device, BluetoothProfile.STATE_CONNECTED); + mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */); } - AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData( - activeDevices.get(0), null, new BluetoothProfileConnectionInfo(profile), - "mBluetoothProfileServiceListener"); - AudioDeviceBroker.BtDeviceInfo info = - mDeviceBroker.createBtDeviceInfo(data, activeDevices.get(0), - BluetoothProfile.STATE_CONNECTED); - mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */); } // @GuardedBy("mDeviceBroker.mSetModeLock") @@ -678,8 +679,11 @@ public class BtHelper { if (adapter != null) { List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.HEADSET); - if (activeDevices.size() > 0 && activeDevices.get(0) != null) { - onSetBtScoActiveDevice(activeDevices.get(0)); + for (BluetoothDevice device : activeDevices) { + if (device == null) { + continue; + } + onSetBtScoActiveDevice(device); } } else { Log.e(TAG, "onHeadsetProfileConnected: Null BluetoothAdapter"); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index fbac924be283..9cf9119aff82 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -3390,17 +3390,10 @@ public final class DisplayManagerService extends SystemService { // with the corresponding displaydevice. HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display); - if (mConfigParameterProvider.isNewPowerControllerFeatureEnabled()) { - displayPowerController = new DisplayPowerController2( - mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, - mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, - () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags); - } else { - displayPowerController = new DisplayPowerController( - mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, - mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, - () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags); - } + displayPowerController = new DisplayPowerController( + mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, + mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, + () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags); mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController); return displayPowerController; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 734381b1ddb0..087cacf9a570 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * 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. @@ -17,11 +17,12 @@ package com.android.server.display; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; +import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString; import android.animation.Animator; import android.animation.ObjectAnimator; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; @@ -31,8 +32,6 @@ import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; @@ -45,6 +44,7 @@ import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.metrics.LogMaker; import android.net.Uri; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; @@ -56,12 +56,12 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.util.FloatProperty; +import android.util.IndentingPrintWriter; import android.util.MathUtils; import android.util.MutableFloat; import android.util.MutableInt; import android.util.Slog; import android.util.SparseArray; -import android.util.TimeUtils; import android.view.Display; import com.android.internal.R; @@ -78,10 +78,15 @@ import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.brightness.BrightnessReason; +import com.android.server.display.brightness.BrightnessUtils; +import com.android.server.display.brightness.DisplayBrightnessController; +import com.android.server.display.brightness.clamper.BrightnessClamperController; +import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.layout.Layout; +import com.android.server.display.state.DisplayStateController; import com.android.server.display.utils.DebugUtils; import com.android.server.display.utils.SensorUtils; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; @@ -119,12 +124,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked"; private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked"; - private static final String TAG = "DisplayPowerController"; + private static final String TAG = "DisplayPowerController2"; // To enable these logs, run: - // 'adb shell setprop persist.log.tag.DisplayPowerController DEBUG && adb reboot' + // 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot' private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); - - private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false; + private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME = + "Screen on blocked by displayoffload"; // If true, uses the color fade on animation. // We might want to turn this off if we cannot get a guarantee that the screen @@ -138,36 +143,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400; private static final int MSG_UPDATE_POWER_STATE = 1; - private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2; - private static final int MSG_SCREEN_ON_UNBLOCKED = 3; - private static final int MSG_SCREEN_OFF_UNBLOCKED = 4; - private static final int MSG_CONFIGURE_BRIGHTNESS = 5; - private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6; - private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7; - private static final int MSG_IGNORE_PROXIMITY = 8; - private static final int MSG_STOP = 9; - private static final int MSG_UPDATE_BRIGHTNESS = 10; - private static final int MSG_UPDATE_RBC = 11; - private static final int MSG_BRIGHTNESS_RAMP_DONE = 12; - private static final int MSG_STATSD_HBM_BRIGHTNESS = 13; - private static final int MSG_SWITCH_USER = 14; - private static final int MSG_BOOT_COMPLETED = 15; - private static final int MSG_SET_DWBC_STRONG_MODE = 16; - private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 17; - private static final int MSG_SET_DWBC_LOGGING_ENABLED = 18; - - private static final int PROXIMITY_UNKNOWN = -1; - private static final int PROXIMITY_NEGATIVE = 0; - private static final int PROXIMITY_POSITIVE = 1; - - // Proximity sensor debounce delay in milliseconds for positive or negative transitions. - private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0; - private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250; + private static final int MSG_SCREEN_ON_UNBLOCKED = 2; + private static final int MSG_SCREEN_OFF_UNBLOCKED = 3; + private static final int MSG_CONFIGURE_BRIGHTNESS = 4; + private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 5; + private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 6; + private static final int MSG_STOP = 7; + private static final int MSG_UPDATE_BRIGHTNESS = 8; + private static final int MSG_UPDATE_RBC = 9; + private static final int MSG_BRIGHTNESS_RAMP_DONE = 10; + private static final int MSG_STATSD_HBM_BRIGHTNESS = 11; + private static final int MSG_SWITCH_USER = 12; + private static final int MSG_BOOT_COMPLETED = 13; + private static final int MSG_SET_DWBC_STRONG_MODE = 14; + private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15; + private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16; + private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17; + private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18; + + private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500; - // Trigger proximity if distance is less than 5 cm. - private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; // State machine constants for tracking initial brightness ramp skipping when enabled. private static final int RAMP_STATE_SKIP_NONE = 0; @@ -181,6 +178,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3; private static final int RINGBUFFER_MAX = 100; + private static final int RINGBUFFER_RBC_MAX = 20; private static final float[] BRIGHTNESS_RANGE_BOUNDARIES = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, @@ -236,10 +234,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Our handler. private final DisplayControllerHandler mHandler; - // Asynchronous callbacks into the power manager service. - // Only invoked from the handler thread while no locks are held. - private final DisplayPowerCallbacks mCallbacks; - // Battery stats. @Nullable private final IBatteryStats mBatteryStats; @@ -253,10 +247,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The display blanker. private final DisplayBlanker mBlanker; - // The LogicalDisplay tied to this DisplayPowerController. + // The LogicalDisplay tied to this DisplayPowerController2. private final LogicalDisplay mLogicalDisplay; - // The ID of the LogicalDisplay tied to this DisplayPowerController. + // The ID of the LogicalDisplay tied to this DisplayPowerController2. private final int mDisplayId; // The ID of the display which this display follows for brightness purposes. @@ -272,34 +266,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Tracker for brightness settings changes. private final SettingsObserver mSettingsObserver; - // The proximity sensor, or null if not available or needed. - private Sensor mProximitySensor; - // The doze screen brightness. private final float mScreenBrightnessDozeConfig; - // The dim screen brightness. - private final float mScreenBrightnessDimConfig; - - // The minimum dim amount to use if the screen brightness is already below - // mScreenBrightnessDimConfig. - private final float mScreenBrightnessMinimumDimAmount; - - private final float mScreenBrightnessDefault; - // True if auto-brightness should be used. private boolean mUseSoftwareAutoBrightnessConfig; - // True if should use light sensor to automatically determine doze screen brightness. - private final boolean mAllowAutoBrightnessWhileDozingConfig; - - // True if we want to persist the brightness value in nits even if the underlying display - // device changes. - private final boolean mPersistBrightnessNitsForDefaultDisplay; - - // True if the brightness config has changed and the short-term model needs to be reset - private boolean mShouldResetShortTermModel; - // Whether or not the color fade on screen on / off is enabled. private final boolean mColorFadeEnabled; @@ -340,10 +312,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @GuardedBy("mLock") private DisplayPowerRequest mPendingRequestLocked; - // True if a request has been made to wait for the proximity sensor to go negative. - @GuardedBy("mLock") - private boolean mPendingWaitForNegativeProximityLocked; - // True if the pending power request or wait for negative proximity flag // has been changed since the last update occurred. @GuardedBy("mLock") @@ -370,67 +338,36 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Must only be accessed on the handler thread. private DisplayPowerState mPowerState; - // True if the device should wait for negative proximity sensor before - // waking up the screen. This is set to false as soon as a negative - // proximity sensor measurement is observed or when the device is forced to - // go to sleep by the user. While true, the screen remains off. - private boolean mWaitingForNegativeProximity; - - // True if the device should not take into account the proximity sensor - // until either the proximity sensor state changes, or there is no longer a - // request to listen to proximity sensor. - private boolean mIgnoreProximityUntilChanged; - - // The actual proximity sensor threshold value. - private float mProximityThreshold; - // Set to true if the proximity sensor listener has been registered - // with the sensor manager. - private boolean mProximitySensorEnabled; - - // The debounced proximity sensor state. - private int mProximity = PROXIMITY_UNKNOWN; - - // The raw non-debounced proximity sensor state. - private int mPendingProximity = PROXIMITY_UNKNOWN; - private long mPendingProximityDebounceTime = -1; // -1 if fully debounced - - // True if the screen was turned off because of the proximity sensor. - // When the screen turns on again, we report user activity to the power manager. - private boolean mScreenOffBecauseOfProximity; // The currently active screen on unblocker. This field is non-null whenever // we are waiting for a callback to release it and unblock the screen. private ScreenOnUnblocker mPendingScreenOnUnblocker; private ScreenOffUnblocker mPendingScreenOffUnblocker; + private Runnable mPendingScreenOnUnblockerByDisplayOffload; // True if we were in the process of turning off the screen. // This allows us to recover more gracefully from situations where we abort // turning off the screen. private boolean mPendingScreenOff; - // True if we have unfinished business and are holding a suspend blocker. - private boolean mUnfinishedBusiness; - // The elapsed real time when the screen on was blocked. private long mScreenOnBlockStartRealTime; private long mScreenOffBlockStartRealTime; + private long mScreenOnBlockByDisplayOffloadStartRealTime; // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields. private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED; + // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on. + // This value is reset when screen on is reported or the blocking is cancelled. + private boolean mScreenTurningOnWasBlockedByDisplayOffload; + // If the last recorded screen state was dozing or not. private boolean mDozing; - // Remembers whether certain kinds of brightness adjustments - // were recently applied so that we can decide how to transition. - private boolean mAppliedAutoBrightness; private boolean mAppliedDimming; - private boolean mAppliedLowPower; - private boolean mAppliedScreenBrightnessOverride; - private boolean mAppliedTemporaryBrightness; - private boolean mAppliedTemporaryAutoBrightnessAdjustment; - private boolean mAppliedBrightnessBoost; + private boolean mAppliedThrottling; // Reason for which the brightness was last changed. See {@link BrightnessReason} for more @@ -456,7 +393,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final boolean mSkipScreenOnBrightnessRamp; // Display white balance components. - // Critical methods must be called on DPC handler thread. + // Critical methods must be called on DPC2 handler thread. @Nullable private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings; @Nullable @@ -468,21 +405,39 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final BrightnessRangeController mBrightnessRangeController; - @Nullable - private final HighBrightnessModeMetadata mHighBrightnessModeMetadata; - private final BrightnessThrottler mBrightnessThrottler; - private final BrightnessSetting mBrightnessSetting; + private final BrightnessClamperController mBrightnessClamperController; private final Runnable mOnBrightnessChangeRunnable; private final BrightnessEvent mLastBrightnessEvent; private final BrightnessEvent mTempBrightnessEvent; + private final DisplayBrightnessController mDisplayBrightnessController; + // Keeps a record of brightness changes for dumpsys. private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer; + // Keeps a record of rbc changes for dumpsys. + private final RingBuffer<BrightnessEvent> mRbcEventRingBuffer = + new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_RBC_MAX); + + // Controls and tracks all the wakelocks that are acquired/released by the system. Also acts as + // a medium of communication between this class and the PowerManagerService. + private final WakelockController mWakelockController; + + // Tracks and manages the proximity state of the associated display. + private final DisplayPowerProximityStateController mDisplayPowerProximityStateController; + + // Tracks and manages the display state of the associated display. + private final DisplayStateController mDisplayStateController; + + + // Responsible for evaluating and tracking the automatic brightness relevant states. + // Todo: This is a temporary workaround. Ideally DPC2 should never talk to the strategies + private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy; + // A record of state for skipping brightness ramps. private int mSkipRampState = RAMP_STATE_SKIP_NONE; @@ -500,81 +455,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private Sensor mLightSensor; private Sensor mScreenOffBrightnessSensor; - // The current brightness configuration. - @Nullable - private BrightnessConfiguration mBrightnessConfiguration; - - // The last brightness that was set by the user and not temporary. Set to - // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded. - private float mLastUserSetScreenBrightness = Float.NaN; - - // The screen brightness setting has changed but not taken effect yet. If this is different - // from the current screen brightness setting then this is coming from something other than us - // and should be considered a user interaction. - private float mPendingScreenBrightnessSetting; - - // The last observed screen brightness setting, either set by us or by the settings app on - // behalf of the user. - private float mCurrentScreenBrightnessSetting; - - // The temporary screen brightness. Typically set when a user is interacting with the - // brightness slider but hasn't settled on a choice yet. Set to - // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary brightness set. - private float mTemporaryScreenBrightness; - - // This brightness value is set in concurrent displays mode. It is the brightness value - // of the lead display that this DPC should follow. - private float mBrightnessToFollow; - - // Indicates whether we should ramp slowly to the brightness value to follow. - private boolean mBrightnessToFollowSlowChange; - - // The last auto brightness adjustment that was set by the user and not temporary. Set to - // Float.NaN when an auto-brightness adjustment hasn't been recorded yet. - private float mAutoBrightnessAdjustment; - - // The pending auto brightness adjustment that will take effect on the next power state update. - private float mPendingAutoBrightnessAdjustment; - - // The temporary auto brightness adjustment. Typically set when a user is interacting with the - // adjustment slider but hasn't settled on a choice yet. Set to - // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set. - private float mTemporaryAutoBrightnessAdjustment; - - private boolean mUseAutoBrightness; - private boolean mIsRbcActive; - // Whether there's a callback to tell listeners the display has changed scheduled to run. When - // true it implies a wakelock is being held to guarantee the update happens before we collapse - // into suspend and so needs to be cleaned up if the thread is exiting. - // Should only be accessed on the Handler thread. - private boolean mOnStateChangedPending; - - // Count of proximity messages currently on this DPC's Handler. Used to keep track of how many - // suspend blocker acquisitions are pending when shutting down this DPC. - // Should only be accessed on the Handler thread. - private int mOnProximityPositiveMessages; - private int mOnProximityNegativeMessages; - // Animators. private ObjectAnimator mColorFadeOnAnimator; private ObjectAnimator mColorFadeOffAnimator; private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; - private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener; - // True if this DisplayPowerController has been stopped and should no longer be running. + // True if this DisplayPowerController2 has been stopped and should no longer be running. private boolean mStopped; private DisplayDeviceConfig mDisplayDeviceConfig; - // Identifiers for suspend blocker acquisition requests - private final String mSuspendBlockerIdUnfinishedBusiness; - private final String mSuspendBlockerIdOnStateChanged; - private final String mSuspendBlockerIdProxPositive; - private final String mSuspendBlockerIdProxNegative; - private final String mSuspendBlockerIdProxDebounce; - private boolean mIsEnabled; private boolean mIsInTransition; private boolean mIsDisplayInternal; @@ -585,13 +477,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there // is one lead display, the additional displays follow the brightness value of the lead display. @GuardedBy("mLock") - private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers = - new SparseArray<>(); + private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers = + new SparseArray(); private boolean mBootCompleted; private final DisplayManagerFlags mFlags; - private int mDozeStateOverride = Display.STATE_UNKNOWN; - private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; + + private DisplayOffloadSession mDisplayOffloadSession; /** * Creates the display power controller. @@ -607,26 +499,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mClock = mInjector.getClock(); mLogicalDisplay = logicalDisplay; mDisplayId = mLogicalDisplay.getDisplayIdLocked(); - mTag = TAG + "[" + mDisplayId + "]"; - mHighBrightnessModeMetadata = hbmMetadata; - mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId); - mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId); - mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId); - mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId); - mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId); - - mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); - mDisplayStatsId = mUniqueDisplayId.hashCode(); + mSensorManager = sensorManager; + mHandler = new DisplayControllerHandler(handler.getLooper()); + mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked() + .getDisplayDeviceConfig(); mIsEnabled = logicalDisplay.isEnabledLocked(); mIsInTransition = logicalDisplay.isInTransitionLocked(); mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked() .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; - mHandler = new DisplayControllerHandler(handler.getLooper()); - mLastBrightnessEvent = new BrightnessEvent(mDisplayId); - mTempBrightnessEvent = new BrightnessEvent(mDisplayId); + mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks); + mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController( + mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(), + () -> updatePowerState(), mDisplayId, mSensorManager); + mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController); + mTag = TAG + "[" + mDisplayId + "]"; mThermalBrightnessThrottlingDataId = logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; + mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); + mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + mDisplayStatsId = mUniqueDisplayId.hashCode(); + + mLastBrightnessEvent = new BrightnessEvent(mDisplayId); + mTempBrightnessEvent = new BrightnessEvent(mDisplayId); if (mDisplayId == Display.DEFAULT_DISPLAY) { mBatteryStats = BatteryStatsService.getService(); @@ -635,14 +529,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } mSettingsObserver = new SettingsObserver(mHandler); - mCallbacks = callbacks; - mSensorManager = sensorManager; mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class); mBlanker = blanker; mContext = context; mBrightnessTracker = brightnessTracker; - // TODO: b/186428377 update brightness setting when display changes - mBrightnessSetting = brightnessSetting; mOnBrightnessChangeRunnable = onBrightnessChangeRunnable; PowerManager pm = context.getSystemService(PowerManager.class); @@ -650,30 +540,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final Resources resources = context.getResources(); // DOZE AND DIM SETTINGS - mScreenBrightnessDozeConfig = clampAbsoluteBrightness( + mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness( pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)); - mScreenBrightnessDimConfig = clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)); - mScreenBrightnessMinimumDimAmount = resources.getFloat( - com.android.internal.R.dimen.config_screenBrightnessMinimumDimAmountFloat); - - - // NORMAL SCREEN SETTINGS - mScreenBrightnessDefault = clampAbsoluteBrightness( - mLogicalDisplay.getDisplayInfoLocked().brightnessDefault); - - mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing); - - mPersistBrightnessNitsForDefaultDisplay = resources.getBoolean( - com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay); - - mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceConfig(); - loadBrightnessRampRates(); mSkipScreenOnBrightnessRamp = resources.getBoolean( - com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); + R.bool.config_skipScreenOnBrightnessRamp); + Runnable modeChangeCallback = () -> { sendUpdatePowerState(); postBrightnessChangeRunnable(); @@ -683,23 +555,38 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } }; - HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback); + HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata, + modeChangeCallback); + mBrightnessThrottler = createBrightnessThrottlerLocked(); - mBrightnessRangeController = new BrightnessRangeController(hbmController, + mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController, modeChangeCallback, mDisplayDeviceConfig, mHandler, flags, mDisplayDevice.getDisplayTokenLocked(), mDisplayDevice.getDisplayDeviceInfoLocked()); - mBrightnessThrottler = createBrightnessThrottlerLocked(); + mDisplayBrightnessController = + new DisplayBrightnessController(context, null, + mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, + brightnessSetting, () -> postBrightnessChangeRunnable(), + new HandlerExecutor(mHandler), flags); + mBrightnessClamperController = mInjector.getBrightnessClamperController( + mHandler, modeChangeCallback::run, + new BrightnessClamperController.DisplayDeviceData( + mUniqueDisplayId, + mThermalBrightnessThrottlingDataId, + logicalDisplay.getPowerThrottlingDataIdLocked(), + mDisplayDeviceConfig), mContext, flags); // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); + mAutomaticBrightnessStrategy = + mDisplayBrightnessController.getAutomaticBrightnessStrategy(); DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; if (mDisplayId == Display.DEFAULT_DISPLAY) { try { - displayWhiteBalanceController = injector.getDisplayWhiteBalanceController( + displayWhiteBalanceController = mInjector.getDisplayWhiteBalanceController( mHandler, mSensorManager, resources); displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); displayWhiteBalanceSettings.setCallbacks(this); @@ -715,21 +602,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mDisplayId == Display.DEFAULT_DISPLAY) { mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); - boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { - @Override - public void onReduceBrightColorsActivationChanged(boolean activated, - boolean userInitiated) { - applyReduceBrightColorsSplineAdjustment(); - - } - - @Override - public void onReduceBrightColorsStrengthChanged(int strength) { + if (mCdsi != null) { + boolean active = mCdsi.setReduceBrightColorsListener( + new ReduceBrightColorsListener() { + @Override + public void onReduceBrightColorsActivationChanged(boolean activated, + boolean userInitiated) { + applyReduceBrightColorsSplineAdjustment(); + + } + + @Override + public void onReduceBrightColorsStrengthChanged(int strength) { + applyReduceBrightColorsSplineAdjustment(); + } + }); + if (active) { applyReduceBrightColorsSplineAdjustment(); } - }); - if (active) { - applyReduceBrightColorsSplineAdjustment(); } } else { mCdsi = null; @@ -737,27 +627,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call setUpAutoBrightness(context, handler); - mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic() + mColorFadeEnabled = mInjector.isColorFadeEnabled() && !resources.getBoolean( com.android.internal.R.bool.config_displayColorFadeDisabled); mColorFadeFadesConfig = resources.getBoolean( - com.android.internal.R.bool.config_animateScreenLights); + R.bool.config_animateScreenLights); mDisplayBlanksAfterDozeConfig = resources.getBoolean( - com.android.internal.R.bool.config_displayBlanksAfterDoze); + R.bool.config_displayBlanksAfterDoze); mBrightnessBucketsInDozeConfig = resources.getBoolean( - com.android.internal.R.bool.config_displayBrightnessBucketsInDoze); - - loadProximitySensor(); - - loadNitBasedBrightnessSetting(); - mBrightnessToFollow = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); - mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; + R.bool.config_displayBrightnessBucketsInDoze); mBootCompleted = bootCompleted; } @@ -785,7 +665,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call */ @Override public boolean isProximitySensorAvailable() { - return mProximitySensor != null; + return mDisplayPowerProximityStateController.isProximitySensorAvailable(); } /** @@ -818,64 +698,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override - public int getLeadDisplayId() { - return mLeadDisplayId; - } - - @Override - public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, - boolean slowChange) { - mBrightnessRangeController.onAmbientLuxChange(ambientLux); - if (nits == BrightnessMappingStrategy.INVALID_NITS) { - mBrightnessToFollow = leadDisplayBrightness; - } else { - float brightness = getBrightnessFromNits(nits); - if (isValidBrightnessValue(brightness)) { - mBrightnessToFollow = brightness; - } else { - // The device does not support nits - mBrightnessToFollow = leadDisplayBrightness; - } - } - mBrightnessToFollowSlowChange = slowChange; - sendUpdatePowerState(); - } - - @Override - public void addDisplayBrightnessFollower(@NonNull DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower); - sendUpdatePowerStateLocked(); - } - } - - @Override - public void removeDisplayBrightnessFollower(@NonNull DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.remove(follower.getDisplayId()); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - } - - @GuardedBy("mLock") - private void clearDisplayBrightnessFollowersLocked() { - for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - mDisplayBrightnessFollowers.clear(); - } - @Nullable @Override public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats( @@ -923,13 +745,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return true; } - boolean changed = false; - - if (waitForNegativeProximity - && !mPendingWaitForNegativeProximityLocked) { - mPendingWaitForNegativeProximityLocked = true; - changed = true; - } + boolean changed = mDisplayPowerProximityStateController + .setPendingWaitForNegativeProximityLocked(waitForNegativeProximity); if (mPendingRequestLocked == null) { mPendingRequestLocked = new DisplayPowerRequest(request); @@ -953,18 +770,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void overrideDozeScreenState(int displayState) { - synchronized (mLock) { - if (mDisplayOffloadSession == null || - !DisplayOffloadSession.isSupportedOffloadState(displayState)) { + mHandler.postAtTime(() -> { + if (mDisplayOffloadSession == null + || !(DisplayOffloadSession.isSupportedOffloadState(displayState) + || displayState == Display.STATE_UNKNOWN)) { return; } - mDozeStateOverride = displayState; + mDisplayStateController.overrideDozeScreenState(displayState); sendUpdatePowerState(); - } + }, mClock.uptimeMillis()); } @Override public void setDisplayOffloadSession(DisplayOffloadSession session) { + if (session == mDisplayOffloadSession) { + return; + } + unblockScreenOnByDisplayOffload(); mDisplayOffloadSession = session; } @@ -981,14 +803,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * when displays get swapped on foldable devices. For example, different brightness properties * of each display need to be properly reflected in AutomaticBrightnessController. * - * Make sure DisplayManagerService.mSyncRoot is held when this is called + * Make sure DisplayManagerService.mSyncRoot lock is held when this is called */ @Override public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) { mLeadDisplayId = leadDisplayId; final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); if (device == null) { - Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: " + Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: " + mLogicalDisplay.getDisplayIdLocked()); return; } @@ -1004,6 +826,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; final String thermalBrightnessThrottlingDataId = mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; + final String powerThrottlingDataId = + mLogicalDisplay.getPowerThrottlingDataIdLocked(); + mHandler.postAtTime(() -> { boolean changed = false; if (mDisplayDevice != device) { @@ -1014,9 +839,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig = config; mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; loadFromDisplayDeviceConfig(token, info, hbmMetadata); - loadNitBasedBrightnessSetting(); + mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config); - /// Since the underlying display-device changed, we really don't know the + // Since the underlying display-device changed, we really don't know the // last command that was sent to change it's state. Let's assume it is unknown so // that we trigger a change immediately. mPowerState.resetScreenState(); @@ -1034,7 +859,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mIsEnabled = isEnabled; mIsInTransition = isInTransition; } + mIsDisplayInternal = isDisplayInternal; + // using local variables here, when mBrightnessThrottler is removed, + // mThermalBrightnessThrottlingDataId could be removed as well + // changed = true will be not needed - clampers are maintaining their state and + // will call updatePowerState if needed. + mBrightnessClamperController.onDisplayChanged( + new BrightnessClamperController.DisplayDeviceData(uniqueId, + thermalBrightnessThrottlingDataId, powerThrottlingDataId, config)); + if (changed) { updatePowerState(); } @@ -1044,7 +878,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** * Unregisters all listeners and interrupts all running threads; halting future work. * - * This method should be called when the DisplayPowerController is no longer in use; i.e. when + * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when * the {@link #mDisplayId display} has been removed. */ @Override @@ -1060,9 +894,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.stop(); } - if (mBrightnessSetting != null) { - mBrightnessSetting.unregisterListener(mBrightnessSettingListener); - } + mDisplayBrightnessController.stop(); mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); } @@ -1073,11 +905,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // All properties that depend on the associated DisplayDevice and the DDC must be // updated here. loadBrightnessRampRates(); - loadProximitySensor(); loadNitsRange(mContext.getResources()); setUpAutoBrightness(mContext, mHandler); reloadReduceBrightColours(); setAnimatorRampSpeeds(/* isIdleMode= */ false); + mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig); mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(), @@ -1126,22 +958,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call noteScreenBrightness(mPowerState.getScreenBrightness()); // Initialize all of the brightness tracking state - final float brightness = convertToAdjustedNits(mPowerState.getScreenBrightness()); + final float brightness = mDisplayBrightnessController.convertToAdjustedNits( + mPowerState.getScreenBrightness()); if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) { mBrightnessTracker.start(brightness); } - mBrightnessSettingListener = brightnessValue -> { + + BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> { Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue); mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); }; + mDisplayBrightnessController + .registerBrightnessSettingChangeListener(brightnessSettingListener); - mBrightnessSetting.registerListener(mBrightnessSettingListener); mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); + if (mFlags.areAutoBrightnessModesEnabled()) { + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS), + /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT); + } handleBrightnessModeChange(); } @@ -1165,12 +1005,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (isIdleScreenBrightnessEnabled) { BrightnessMappingStrategy idleModeBrightnessMapper = BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, - AUTO_BRIGHTNESS_MODE_IDLE, mDisplayWhiteBalanceController); + AUTO_BRIGHTNESS_MODE_IDLE, + mDisplayWhiteBalanceController); if (idleModeBrightnessMapper != null) { - brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, idleModeBrightnessMapper); + brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, + idleModeBrightnessMapper); } } + BrightnessMappingStrategy dozeModeBrightnessMapper = + BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, + AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController); + if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) { + brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper); + } + float userLux = BrightnessMappingStrategy.INVALID_LUX; float userNits = BrightnessMappingStrategy.INVALID_NITS; if (mAutomaticBrightnessController != null) { @@ -1180,7 +1029,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (defaultModeBrightnessMapper != null) { final float dozeScaleFactor = context.getResources().getFraction( - com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, + R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); // Ambient Lux - Active Mode Brightness Thresholds @@ -1264,14 +1113,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call long darkeningLightDebounceIdle = mDisplayDeviceConfig .getAutoBrightnessDarkeningLightDebounceIdle(); boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean( - com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); + R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); int lightSensorWarmUpTimeConfig = context.getResources().getInteger( - com.android.internal.R.integer.config_lightSensorWarmupTime); + R.integer.config_lightSensorWarmupTime); int lightSensorRate = context.getResources().getInteger( - com.android.internal.R.integer.config_autoBrightnessLightSensorRate); + R.integer.config_autoBrightnessLightSensorRate); int initialLightSensorRate = context.getResources().getInteger( - com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate); + R.integer.config_autoBrightnessInitialLightSensorRate); if (initialLightSensorRate == -1) { initialLightSensorRate = lightSensorRate; } else if (initialLightSensorRate > lightSensorRate) { @@ -1301,7 +1150,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(), mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits); + mDisplayBrightnessController.setAutomaticBrightnessController( + mAutomaticBrightnessController); + mAutomaticBrightnessStrategy + .setAutomaticBrightnessController(mAutomaticBrightnessController); mBrightnessEventRingBuffer = new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); @@ -1351,7 +1204,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else { Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back"); mNitsRange = BrightnessMappingStrategy.getFloatArray(resources - .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits)); + .obtainTypedArray(R.array.config_screenBrightnessNits)); } } @@ -1369,7 +1222,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.switchMode(mode); setAnimatorRampSpeeds(isIdle); } - Message msg = mHandler.obtainMessage(); msg.what = MSG_SET_DWBC_STRONG_MODE; msg.arg1 = isIdle ? 1 : 0; @@ -1421,28 +1273,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ private void cleanupHandlerThreadAfterStop() { - setProximitySensorEnabled(false); + mDisplayPowerProximityStateController.cleanup(); mBrightnessRangeController.stop(); mBrightnessThrottler.stop(); + mBrightnessClamperController.stop(); mHandler.removeCallbacksAndMessages(null); // Release any outstanding wakelocks we're still holding because of pending messages. - if (mUnfinishedBusiness) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness); - mUnfinishedBusiness = false; - } - if (mOnStateChangedPending) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged); - mOnStateChangedPending = false; - } - for (int i = 0; i < mOnProximityPositiveMessages; i++) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive); - } - mOnProximityPositiveMessages = 0; - for (int i = 0; i < mOnProximityNegativeMessages; i++) { - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative); - } - mOnProximityNegativeMessages = 0; + mWakelockController.releaseAll(); final float brightness = mPowerState != null ? mPowerState.getScreenBrightness() @@ -1457,10 +1295,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.stop(); } + if (mDisplayWhiteBalanceController != null) { mDisplayWhiteBalanceController.setEnabled(false); } - } // Call from handler thread @@ -1476,7 +1314,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean mustNotify; final int previousPolicy; boolean mustInitialize = false; - int brightnessAdjustmentFlags = 0; mBrightnessReasonTemp.set(null); mTempBrightnessEvent.reset(); SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers; @@ -1491,7 +1328,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mPowerRequest == null) { mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); - updatePendingProximityRequestsLocked(); + mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mustInitialize = true; // Assume we're on and bright until told otherwise, since that's the state we turn @@ -1500,7 +1337,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else if (mPendingRequestChangedLocked) { previousPolicy = mPowerRequest.policy; mPowerRequest.copyFrom(mPendingRequestLocked); - updatePendingProximityRequestsLocked(); + mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mDisplayReadyLocked = false; } else { @@ -1512,105 +1349,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call displayBrightnessFollowers = mDisplayBrightnessFollowers.clone(); } - // Compute the basic display state using the policy. - // We might override this below based on other factors. - // Initialise brightness as invalid. - int state; - float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; - boolean performScreenOffTransition = false; - switch (mPowerRequest.policy) { - case DisplayPowerRequest.POLICY_OFF: - state = Display.STATE_OFF; - performScreenOffTransition = true; - break; - case DisplayPowerRequest.POLICY_DOZE: - if (mDozeStateOverride != Display.STATE_UNKNOWN) { - state = mDozeStateOverride; - } else if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) { - state = mPowerRequest.dozeScreenState; - } else { - state = Display.STATE_DOZE; - } - if (!mAllowAutoBrightnessWhileDozingConfig) { - brightnessState = mPowerRequest.dozeScreenBrightness; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE); - } - break; - case DisplayPowerRequest.POLICY_DIM: - case DisplayPowerRequest.POLICY_BRIGHT: - default: - state = Display.STATE_ON; - break; - } - assert (state != Display.STATE_UNKNOWN); - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness - && mIsEnabled && (state == Display.STATE_OFF || (state == Display.STATE_DOZE - && !mAllowAutoBrightnessWhileDozingConfig)) - && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); - } - - boolean skipRampBecauseOfProximityChangeToNegative = false; - // Apply the proximity sensor. - if (mProximitySensor != null) { - if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) { - // At this point the policy says that the screen should be on, but we've been - // asked to listen to the prox sensor to adjust the display state, so lets make - // sure the sensor is on. - setProximitySensorEnabled(true); - if (!mScreenOffBecauseOfProximity - && mProximity == PROXIMITY_POSITIVE - && !mIgnoreProximityUntilChanged) { - // Prox sensor already reporting "near" so we should turn off the screen. - // Also checked that we aren't currently set to ignore the proximity sensor - // temporarily. - mScreenOffBecauseOfProximity = true; - sendOnProximityPositiveWithWakelock(); - } - } else if (mWaitingForNegativeProximity - && mScreenOffBecauseOfProximity - && mProximity == PROXIMITY_POSITIVE - && state != Display.STATE_OFF) { - // The policy says that we should have the screen on, but it's off due to the prox - // and we've been asked to wait until the screen is far from the user to turn it - // back on. Let keep the prox sensor on so we can tell when it's far again. - setProximitySensorEnabled(true); - } else { - // We haven't been asked to use the prox sensor and we're not waiting on the screen - // to turn back on...so lets shut down the prox sensor. - setProximitySensorEnabled(false); - mWaitingForNegativeProximity = false; - } - - if (mScreenOffBecauseOfProximity - && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) { - // The screen *was* off due to prox being near, but now it's "far" so lets turn - // the screen back on. Also turn it back on if we've been asked to ignore the - // prox sensor temporarily. - mScreenOffBecauseOfProximity = false; - skipRampBecauseOfProximityChangeToNegative = true; - sendOnProximityNegativeWithWakelock(); - } - } else { - setProximitySensorEnabled(false); - mWaitingForNegativeProximity = false; - mIgnoreProximityUntilChanged = false; - - if (mScreenOffBecauseOfProximity) { - // The screen *was* off due to prox being near, but now there's no prox sensor, so - // let's turn the screen back on. - mScreenOffBecauseOfProximity = false; - skipRampBecauseOfProximityChangeToNegative = true; - sendOnProximityNegativeWithWakelock(); - } - } - - if (!mIsEnabled - || mIsInTransition - || mScreenOffBecauseOfProximity) { - state = Display.STATE_OFF; - } + int state = mDisplayStateController + .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition); // Initialize things the first time the power state is changed. if (mustInitialize) { @@ -1620,157 +1360,100 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Animate the screen state change unless already animating. // The transition may be deferred, so after this point we will use the // actual state instead of the desired one. - final int oldState = mPowerState.getScreenState(); - animateScreenStateChange(state, performScreenOffTransition); + animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition()); state = mPowerState.getScreenState(); - boolean slowChange = false; - if (state == Display.STATE_OFF) { - brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF); + // Switch to doze auto-brightness mode if needed + if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null + && !mAutomaticBrightnessController.isInIdleMode()) { + setAutomaticScreenBrightnessMode(Display.isDozeState(state) + ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); } - if (Float.isNaN(brightnessState) && isValidBrightnessValue(mBrightnessToFollow)) { - brightnessState = mBrightnessToFollow; - slowChange = mBrightnessToFollowSlowChange; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_FOLLOWER); - } + final boolean userSetBrightnessChanged = mDisplayBrightnessController + .updateUserSetScreenBrightness(); - if ((Float.isNaN(brightnessState)) - && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) { - brightnessState = mPowerRequest.screenBrightnessOverride; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE); - mAppliedScreenBrightnessOverride = true; - } else { - mAppliedScreenBrightnessOverride = false; - } - - final boolean autoBrightnessEnabledInDoze = - mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); - final boolean autoBrightnessEnabled = mUseAutoBrightness - && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) - && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_OVERRIDE - && mAutomaticBrightnessController != null; - final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness - && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); - final int autoBrightnessState = autoBrightnessEnabled - && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : autoBrightnessDisabledDueToDisplayOff - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; - - final boolean userSetBrightnessChanged = updateUserSetScreenBrightness(); + DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController + .updateBrightness(mPowerRequest, state); + float brightnessState = displayBrightnessState.getBrightness(); + float rawBrightnessState = displayBrightnessState.getBrightness(); + mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason()); + boolean slowChange = displayBrightnessState.isSlowChange(); + // custom transition duration + float customAnimationRate = displayBrightnessState.getCustomAnimationRate(); - // Use the temporary screen brightness if there isn't an override, either from - // WindowManager or based on the display state. - if (isValidBrightnessValue(mTemporaryScreenBrightness)) { - brightnessState = mTemporaryScreenBrightness; - mAppliedTemporaryBrightness = true; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY); - } else { - mAppliedTemporaryBrightness = false; + // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor + // doesn't yet have a valid lux value to use with auto-brightness. + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController + .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness() + && mIsEnabled && (state == Display.STATE_OFF + || (state == Display.STATE_DOZE + && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())) + && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); } - final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment(); - - // Use the autobrightness adjustment override if set. - final float autoBrightnessAdjustment; - if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) { - autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment; - brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP; - mAppliedTemporaryAutoBrightnessAdjustment = true; - } else { - autoBrightnessAdjustment = mAutoBrightnessAdjustment; - brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO; - mAppliedTemporaryAutoBrightnessAdjustment = false; - } - // Apply brightness boost. - // We do this here after deciding whether auto-brightness is enabled so that we don't - // disable the light sensor during this temporary state. That way when boost ends we will - // be able to resume normal auto-brightness behavior without any delay. - if (mPowerRequest.boostScreenBrightness - && brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT) { - brightnessState = PowerManager.BRIGHTNESS_MAX; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST); - mAppliedBrightnessBoost = true; - } else { - mAppliedBrightnessBoost = false; - } + // Take note if the short term model was already active before applying the current + // request changes. + final boolean wasShortTermModelActive = + mAutomaticBrightnessStrategy.isShortTermModelActive(); + mAutomaticBrightnessStrategy.setAutoBrightnessState(state, + mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(), + mBrightnessReasonTemp.getReason(), mPowerRequest.policy, + mDisplayBrightnessController.getLastUserSetScreenBrightness(), + userSetBrightnessChanged); // If the brightness is already set then it's been overridden by something other than the // user, or is a temporary adjustment. boolean userInitiatedChange = (Float.isNaN(brightnessState)) - && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); - boolean wasShortTermModelActive = false; - // Configure auto-brightness. - if (mAutomaticBrightnessController != null) { - wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints(); - mAutomaticBrightnessController.configure(autoBrightnessState, - mBrightnessConfiguration, - mLastUserSetScreenBrightness, - userSetBrightnessChanged, autoBrightnessAdjustment, - autoBrightnessAdjustmentChanged, mPowerRequest.policy, - mShouldResetShortTermModel); - mShouldResetShortTermModel = false; - } - mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessEnabled + && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged() + || userSetBrightnessChanged); + + mBrightnessRangeController.setAutoBrightnessEnabled( + mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : autoBrightnessDisabledDueToDisplayOff + : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff() ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); - if (mBrightnessTracker != null) { - mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null - && mBrightnessConfiguration.shouldCollectColorSamples()); - } - - boolean updateScreenBrightnessSetting = false; - float rawBrightnessState = brightnessState; - + boolean updateScreenBrightnessSetting = + displayBrightnessState.shouldUpdateScreenBrightnessSetting(); + float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness(); // Apply auto-brightness. + int brightnessAdjustmentFlags = 0; if (Float.isNaN(brightnessState)) { - float newAutoBrightnessAdjustment = autoBrightnessAdjustment; - if (autoBrightnessEnabled) { - rawBrightnessState = mAutomaticBrightnessController - .getRawAutomaticScreenBrightness(); - brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness( + if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) { + brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness( mTempBrightnessEvent); - newAutoBrightnessAdjustment = - mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment(); - } - if (isValidBrightnessValue(brightnessState) - || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { - // Use current auto-brightness value and slowly adjust to changes. - brightnessState = clampScreenBrightness(brightnessState); - if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) { - slowChange = true; // slowly adapt to auto-brightness - } - updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; - mAppliedAutoBrightness = true; - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.setLightSensorEnabled(false); + if (BrightnessUtils.isValidBrightnessValue(brightnessState) + || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { + rawBrightnessState = mAutomaticBrightnessController + .getRawAutomaticScreenBrightness(); + brightnessState = clampScreenBrightness(brightnessState); + // slowly adapt to auto-brightness + // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness + slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness() + && !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged(); + brightnessAdjustmentFlags = + mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentReasonsFlags(); + updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState; + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true); + mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.setLightSensorEnabled(false); + } + } else { + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); } - } else { - mAppliedAutoBrightness = false; - } - if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) { - // If the autobrightness controller has decided to change the adjustment value - // used, make sure that's reflected in settings. - putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment); - } else { - // Adjustment values resulted in no change - brightnessAdjustmentFlags = 0; } } else { // Any non-auto-brightness values such as override or temporary should still be subject // to clamping so that they don't go beyond the current max as specified by HBM // Controller. brightnessState = clampScreenBrightness(brightnessState); - mAppliedAutoBrightness = false; - brightnessAdjustmentFlags = 0; + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); } + // Use default brightness when dozing unless overridden. if ((Float.isNaN(brightnessState)) && Display.isDozeState(state)) { @@ -1781,14 +1464,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The ALS is not available yet - use the screen off sensor to determine the initial // brightness - if (Float.isNaN(brightnessState) && autoBrightnessEnabled + if (Float.isNaN(brightnessState) && mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() && mScreenOffBrightnessSensorController != null) { rawBrightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness(); brightnessState = rawBrightnessState; - if (isValidBrightnessValue(brightnessState)) { + if (BrightnessUtils.isValidBrightnessValue(brightnessState)) { brightnessState = clampScreenBrightness(brightnessState); - updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; + updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness() + != brightnessState; mBrightnessReasonTemp.setReason( BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR); } @@ -1796,9 +1480,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Apply manual brightness. if (Float.isNaN(brightnessState)) { - rawBrightnessState = mCurrentScreenBrightnessSetting; + rawBrightnessState = currentBrightnessSetting; brightnessState = clampScreenBrightness(rawBrightnessState); - if (brightnessState != mCurrentScreenBrightnessSetting) { + if (brightnessState != currentBrightnessSetting) { // The manually chosen screen brightness is outside of the currently allowed // range (i.e., high-brightness-mode), make sure we tell the rest of the system // by updating the setting. @@ -1811,7 +1495,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call : mAutomaticBrightnessController.getAmbientLux(); for (int i = 0; i < displayBrightnessFollowers.size(); i++) { DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); - follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState), + follower.setBrightnessToFollow(rawBrightnessState, + mDisplayBrightnessController.convertToNits(rawBrightnessState), ambientLux, slowChange); } @@ -1823,64 +1508,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Note throttling effectively changes the allowed brightness range, so, similarly to HBM, // we broadcast this change through setting. final float unthrottledBrightnessState = brightnessState; - if (mBrightnessThrottler.isThrottled()) { - mTempBrightnessEvent.setThermalMax(mBrightnessThrottler.getBrightnessCap()); - brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap()); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED); - if (!mAppliedThrottling) { - // Brightness throttling is needed, so do so quickly. - // Later, when throttling is removed, we let other mechanisms decide on speed. - slowChange = false; - } - mAppliedThrottling = true; - } else if (mAppliedThrottling) { - mAppliedThrottling = false; - } + DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, + brightnessState, slowChange); + + brightnessState = clampedState.getBrightness(); + slowChange = clampedState.isSlowChange(); + // faster rate wins, at this point customAnimationRate == -1, strategy does not control + // customAnimationRate. Should be revisited if strategy start setting this value + customAnimationRate = Math.max(customAnimationRate, clampedState.getCustomAnimationRate()); + mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier()); if (updateScreenBrightnessSetting) { // Tell the rest of the system about the new brightness in case we had to change it // for things like auto-brightness or high-brightness-mode. Note that we do this - // before applying the low power or dim transformations so that the slider - // accurately represents the full possible range, even if they range changes what - // it means in absolute terms. - updateScreenBrightnessSetting(brightnessState); - } - - // Apply dimming by at least some minimum amount when user activity - // timeout is about to expire. - if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { - brightnessState = Math.max( - Math.min(brightnessState - mScreenBrightnessMinimumDimAmount, - mScreenBrightnessDimConfig), - PowerManager.BRIGHTNESS_MIN); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); - } - if (!mAppliedDimming) { - slowChange = false; - } - mAppliedDimming = true; - } else if (mAppliedDimming) { - slowChange = false; - mAppliedDimming = false; - } - // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor - // as long as it is above the minimum threshold. - if (mPowerRequest.lowPowerMode) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { - final float brightnessFactor = - Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); - final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor); - brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER); - } - if (!mAppliedLowPower) { - slowChange = false; - } - mAppliedLowPower = true; - } else if (mAppliedLowPower) { - slowChange = false; - mAppliedLowPower = false; + // only considering maxBrightness (ignoring brightness modifiers like low power or dim) + // so that the slider accurately represents the full possible range, + // even if they range changes what it means in absolute terms. + mDisplayBrightnessController.updateScreenBrightnessSetting( + MathUtils.constrain(unthrottledBrightnessState, + clampedState.getMinBrightness(), clampedState.getMaxBrightness())); } // The current brightness to use has been calculated at this point, and HbmController should @@ -1889,13 +1535,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // brightness sources (such as an app override) are not saved to the setting, but should be // reflected in HBM calculations. mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, - mBrightnessThrottler.getBrightnessMaxReason()); + mBrightnessClamperController.getBrightnessMaxReason()); // Animate the screen brightness when the screen is on or dozing. - // Skip the animation when the screen is off. + // Skip the animation when the screen is off or suspended. boolean brightnessAdjusted = false; final boolean brightnessIsTemporary = - mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment; + (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY) + || mAutomaticBrightnessStrategy + .isTemporaryAutoBrightnessAdjustmentApplied(); if (!mPendingScreenOff) { if (mSkipScreenOnBrightnessRamp) { if (state == Display.STATE_ON) { @@ -1916,7 +1564,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState - != RAMP_STATE_SKIP_NONE) || skipRampBecauseOfProximityChangeToNegative; + != RAMP_STATE_SKIP_NONE) || mDisplayPowerProximityStateController + .shouldSkipRampBecauseOfProximityChangeToNegative(); // While dozing, sometimes the brightness is split into buckets. Rather than animating // through the buckets, which is unlikely to be smooth in the first place, just jump // right to the suggested brightness. @@ -1950,13 +1599,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // We want to scale HDR brightness level with the SDR level, we also need to restore // SDR brightness immediately when entering dim or low power mode. animateValue = mBrightnessRangeController.getHdrBrightnessValue(); + customAnimationRate = Math.max(customAnimationRate, + mBrightnessRangeController.getHdrTransitionRate()); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR); } + // if doze or suspend state is requested, we want to finish brightnes animation fast + // to allow state animation to start + if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE + && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN // dozing + || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND + || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) { + customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; + slowChange = false; + } + final float currentBrightness = mPowerState.getScreenBrightness(); final float currentSdrBrightness = mPowerState.getSdrScreenBrightness(); - if (isValidBrightnessValue(animateValue) + if (BrightnessUtils.isValidBrightnessValue(animateValue) && (animateValue != currentBrightness || sdrAnimateValue != currentSdrBrightness)) { boolean skipAnimation = initialRampSkip || hasBrightnessBuckets @@ -1986,6 +1647,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (skipAnimation) { animateScreenBrightness(animateValue, sdrAnimateValue, SCREEN_ANIMATION_RATE_MINIMUM); + } else if (customAnimationRate > 0) { + animateScreenBrightness(animateValue, sdrAnimateValue, + customAnimationRate, /* ignoreAnimationLimits = */true); } else { boolean isIncreasing = animateValue > currentBrightness; final float rampSpeed; @@ -2007,13 +1671,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - wasShortTermModelActive, autoBrightnessEnabled, brightnessIsTemporary); + wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(), + brightnessIsTemporary, displayBrightnessState.getShouldUseAutoBrightness()); // We save the brightness info *after* the brightness setting has been changed and // adjustments made so that the brightness info reflects the latest value. - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), animateValue); + brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), + animateValue, clampedState); } else { - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting()); + brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), clampedState); } // Only notify if the brightness adjustment is not temporary (i.e. slider has been released) @@ -2049,13 +1715,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call ? mCdsi.getReduceBrightColorsStrength() : -1); mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); - mTempBrightnessEvent.setAutomaticBrightnessEnabled(mUseAutoBrightness); + mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState + .getDisplayBrightnessStrategyName()); + mTempBrightnessEvent.setAutomaticBrightnessEnabled( + displayBrightnessState.getShouldUseAutoBrightness()); // Temporary is what we use during slider interactions. We avoid logging those so that // we don't spam logcat when the slider is being used. boolean tempToTempTransition = mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY && mLastBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY; + // Purely for dumpsys; + final boolean isRbcEvent = + mLastBrightnessEvent.isRbcEnabled() != mTempBrightnessEvent.isRbcEnabled(); + if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition) || brightnessAdjustmentFlags != 0) { mTempBrightnessEvent.setInitialBrightness(mLastBrightnessEvent.getBrightness()); @@ -2075,6 +1748,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mBrightnessEventRingBuffer != null) { mBrightnessEventRingBuffer.append(newEvent); } + if (isRbcEvent) { + mRbcEventRingBuffer.append(newEvent); + } + } // Update display white-balance. @@ -2092,6 +1769,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // reporting the display is ready because we only need to ensure the screen is in the // right power state even as it continues to converge on the desired brightness. final boolean ready = mPendingScreenOnUnblocker == null + && mPendingScreenOnUnblockerByDisplayOffload == null && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted())) && mPowerState.waitUntilClean(mCleanListener); @@ -2106,12 +1784,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Grab a wake lock if we have unfinished business. - if (!finished && !mUnfinishedBusiness) { - if (DEBUG) { - Slog.d(mTag, "Unfinished business..."); - } - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness); - mUnfinishedBusiness = true; + if (!finished) { + mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); } // Notify the power manager when ready. @@ -2130,12 +1804,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Release the wake lock when we have no unfinished business. - if (finished && mUnfinishedBusiness) { - if (DEBUG) { - Slog.d(mTag, "Finished business..."); - } - mUnfinishedBusiness = false; - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness); + if (finished) { + mWakelockController.releaseWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); } // Record if dozing for future comparison. @@ -2166,9 +1836,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void setDwbcLoggingEnabled(int arg) { if (mDisplayWhiteBalanceController != null) { - final boolean shouldEnable = (arg == 1); - mDisplayWhiteBalanceController.setLoggingEnabled(shouldEnable); - mDisplayWhiteBalanceSettings.setLoggingEnabled(shouldEnable); + final boolean enabled = (arg == 1); + mDisplayWhiteBalanceController.setLoggingEnabled(enabled); + mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled); } } @@ -2183,7 +1853,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call */ @Override public void ignoreProximitySensorUntilChanged() { - mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY); + mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged(); } @Override @@ -2210,21 +1880,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void setBrightnessFromOffload(float brightness) { - // The old DPC is no longer supported + Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD, + Float.floatToIntBits(brightness), 0 /*unused*/); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } @Override public float[] getAutoBrightnessLevels( @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - // The old DPC is no longer supported - return null; + int preset = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); + return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); } @Override public float[] getAutoBrightnessLuxLevels( @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - // The old DPC is no longer supported - return null; + int preset = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); + return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); } @Override @@ -2241,18 +1917,29 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + @Override + public void onBootCompleted() { + Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); + } + private boolean saveBrightnessInfo(float brightness) { - return saveBrightnessInfo(brightness, brightness); + return saveBrightnessInfo(brightness, /* state= */ null); + } + + private boolean saveBrightnessInfo(float brightness, @Nullable DisplayBrightnessState state) { + return saveBrightnessInfo(brightness, brightness, state); } - private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) { + private boolean saveBrightnessInfo(float brightness, float adjustedBrightness, + @Nullable DisplayBrightnessState state) { synchronized (mCachedBrightnessInfo) { - final float minBrightness = Math.min( - mBrightnessRangeController.getCurrentBrightnessMin(), - mBrightnessThrottler.getBrightnessCap()); + float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX; + float stateMin = state != null ? state.getMinBrightness() : PowerManager.BRIGHTNESS_MAX; + final float minBrightness = Math.max(stateMin, Math.min( + mBrightnessRangeController.getCurrentBrightnessMin(), stateMax)); final float maxBrightness = Math.min( - mBrightnessRangeController.getCurrentBrightnessMax(), - mBrightnessThrottler.getBrightnessCap()); + mBrightnessRangeController.getCurrentBrightnessMax(), stateMax); boolean changed = false; changed |= @@ -2275,8 +1962,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessRangeController.getTransitionPoint()); changed |= mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, - mBrightnessThrottler.getBrightnessMaxReason()); - + mBrightnessClamperController.getBrightnessMaxReason()); return changed; } } @@ -2288,27 +1974,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private HighBrightnessModeController createHbmControllerLocked( - Runnable modeChangeCallback) { - final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); - final IBinder displayToken = - mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked(); - final String displayUniqueId = - mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) { + final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig(); + final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked(); + final String displayUniqueId = mDisplayDevice.getUniqueId(); final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; - final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked(); return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height, displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX, hbmData, - new HighBrightnessModeController.HdrBrightnessDeviceConfig() { - @Override - public float getHdrBrightnessFromSdr( - float sdrBrightness, float maxDesiredHdrSdrRatio) { - return mDisplayDeviceConfig.getHdrBrightnessFromSdr( - sdrBrightness, maxDesiredHdrSdrRatio); - } - }, modeChangeCallback, mHighBrightnessModeMetadata, mContext); + PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) -> + mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness, + maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext); } private BrightnessThrottler createBrightnessThrottlerLocked() { @@ -2359,18 +2036,72 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) { + if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) { + return; + } + mScreenTurningOnWasBlockedByDisplayOffload = true; + + Trace.asyncTraceBegin( + Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); + mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime(); + + mPendingScreenOnUnblockerByDisplayOffload = + () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession); + if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) { + mPendingScreenOnUnblockerByDisplayOffload = null; + long delay = + SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; + Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after " + + delay + " ms."); + Trace.asyncTraceEnd( + Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); + return; + } + Slog.i(mTag, "Blocking screen on for offloading."); + } + + private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) { + Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED, + displayOffloadSession); + mHandler.sendMessage(msg); + } + + private void unblockScreenOnByDisplayOffload() { + if (mPendingScreenOnUnblockerByDisplayOffload == null) { + return; + } + mPendingScreenOnUnblockerByDisplayOffload = null; + long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; + Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms"); + Trace.asyncTraceEnd( + Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); + } + private boolean setScreenState(int state) { return setScreenState(state, false /*reportOnly*/); } private boolean setScreenState(int state, boolean reportOnly) { final boolean isOff = (state == Display.STATE_OFF); + final boolean isOn = (state == Display.STATE_ON); + final boolean changed = mPowerState.getScreenState() != state; + + // If the screen is turning on, give displayoffload a chance to do something before the + // screen actually turns on. + // TODO(b/316941732): add tests for this displayoffload screen-on blocker. + if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) { + blockScreenOnByDisplayOffload(mDisplayOffloadSession); + } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) { + // No longer turning screen on, so unblock previous screen on blocking immediately. + unblockScreenOnByDisplayOffload(); + mScreenTurningOnWasBlockedByDisplayOffload = false; + } - if (mPowerState.getScreenState() != state - || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { + if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { // If we are trying to turn screen off, give policy a chance to do something before we // actually turn the screen off. - if (isOff && !mScreenOffBecauseOfProximity) { + if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF); @@ -2383,8 +2114,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - if (!reportOnly && mPowerState.getScreenState() != state - && readyToUpdateDisplayState()) { + if (!reportOnly && changed && readyToUpdateDisplayState() + && mPendingScreenOffUnblocker == null + && mPendingScreenOnUnblockerByDisplayOffload == null) { Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state); String propertyKey = "debug.tracing.screen_state"; @@ -2410,7 +2142,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // it is only removed once the window manager tells us that the activity has // finished drawing underneath. if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF - && !mScreenOffBecauseOfProximity) { + && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); unblockScreenOn(); mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); @@ -2436,12 +2168,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Return true if the screen isn't blocked. - return mPendingScreenOnUnblocker == null; + return mPendingScreenOnUnblocker == null + && mPendingScreenOnUnblockerByDisplayOffload == null; } private void setReportedScreenState(int state) { Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state); mReportedScreenStateToPolicy = state; + if (state == REPORTED_TO_POLICY_SCREEN_ON) { + mScreenTurningOnWasBlockedByDisplayOffload = false; + } } private void loadAmbientLightSensor() { @@ -2456,18 +2192,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK); } - private void loadProximitySensor() { - if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT || mDisplayId != Display.DEFAULT_DISPLAY) { - return; - } - mProximitySensor = SensorUtils.findSensor(mSensorManager, - mDisplayDeviceConfig.getProximitySensor(), Sensor.TYPE_PROXIMITY); - if (mProximitySensor != null) { - mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(), - TYPICAL_PROXIMITY_THRESHOLD); - } - } - private float clampScreenBrightness(float value) { if (Float.isNaN(value)) { value = PowerManager.BRIGHTNESS_MIN; @@ -2476,18 +2200,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessRangeController.getCurrentBrightnessMax()); } - // Checks whether the brightness is within the valid brightness range, not including off. - private boolean isValidBrightnessValue(float brightness) { - return brightness >= PowerManager.BRIGHTNESS_MIN - && brightness <= PowerManager.BRIGHTNESS_MAX; + private void animateScreenBrightness(float target, float sdrTarget, float rate) { + animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false); } - private void animateScreenBrightness(float target, float sdrTarget, float rate) { + private void animateScreenBrightness(float target, float sdrTarget, float rate, + boolean ignoreAnimationLimits) { if (DEBUG) { Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget + ", rate=" + rate); } - if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, false)) { + if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, + ignoreAnimationLimits)) { Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target); String propertyKey = "debug.tracing.screen_brightness"; @@ -2655,102 +2379,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final Runnable mCleanListener = this::sendUpdatePowerState; - private void setProximitySensorEnabled(boolean enable) { - if (enable) { - if (!mProximitySensorEnabled) { - // Register the listener. - // Proximity sensor state already cleared initially. - mProximitySensorEnabled = true; - mIgnoreProximityUntilChanged = false; - mSensorManager.registerListener(mProximitySensorListener, mProximitySensor, - SensorManager.SENSOR_DELAY_NORMAL, mHandler); - } - } else { - if (mProximitySensorEnabled) { - // Unregister the listener. - // Clear the proximity sensor state for next time. - mProximitySensorEnabled = false; - mProximity = PROXIMITY_UNKNOWN; - mIgnoreProximityUntilChanged = false; - mPendingProximity = PROXIMITY_UNKNOWN; - mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); - mSensorManager.unregisterListener(mProximitySensorListener); - clearPendingProximityDebounceTime(); // release wake lock (must be last) - } - } - } - - private void handleProximitySensorEvent(long time, boolean positive) { - if (mProximitySensorEnabled) { - if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) { - return; // no change - } - if (mPendingProximity == PROXIMITY_POSITIVE && positive) { - return; // no change - } - - // Only accept a proximity sensor reading if it remains - // stable for the entire debounce delay. We hold a wake lock while - // debouncing the sensor. - mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); - if (positive) { - mPendingProximity = PROXIMITY_POSITIVE; - setPendingProximityDebounceTime( - time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock - } else { - mPendingProximity = PROXIMITY_NEGATIVE; - setPendingProximityDebounceTime( - time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock - } - - // Debounce the new sensor reading. - debounceProximitySensor(); - } - } - - private void debounceProximitySensor() { - if (mProximitySensorEnabled - && mPendingProximity != PROXIMITY_UNKNOWN - && mPendingProximityDebounceTime >= 0) { - final long now = mClock.uptimeMillis(); - if (mPendingProximityDebounceTime <= now) { - if (mProximity != mPendingProximity) { - // if the status of the sensor changed, stop ignoring. - mIgnoreProximityUntilChanged = false; - Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]"); - } - // Sensor reading accepted. Apply the change then release the wake lock. - mProximity = mPendingProximity; - updatePowerState(); - clearPendingProximityDebounceTime(); // release wake lock (must be last) - } else { - // Need to wait a little longer. - // Debounce again later. We continue holding a wake lock while waiting. - Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED); - mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime); - } - } - } - - private void clearPendingProximityDebounceTime() { - if (mPendingProximityDebounceTime >= 0) { - mPendingProximityDebounceTime = -1; - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxDebounce); - } - } - - private void setPendingProximityDebounceTime(long debounceTime) { - if (mPendingProximityDebounceTime < 0) { - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxDebounce); - } - mPendingProximityDebounceTime = debounceTime; - } - private void sendOnStateChangedWithWakelock() { - if (!mOnStateChangedPending) { - mOnStateChangedPending = true; - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdOnStateChanged); - mHandler.post(mOnStateChangedRunnable); + boolean wakeLockAcquired = mWakelockController.acquireWakelock( + WakelockController.WAKE_LOCK_STATE_CHANGED); + if (wakeLockAcquired) { + mHandler.post(mWakelockController.getOnStateChangedRunnable()); } } @@ -2762,12 +2395,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void handleSettingsChange(boolean userSwitch) { - mPendingScreenBrightnessSetting = getScreenBrightnessSetting(); - mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); + mDisplayBrightnessController + .setPendingScreenBrightness(mDisplayBrightnessController + .getScreenBrightnessSetting()); + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(userSwitch); if (userSwitch) { // Don't treat user switches as user initiated change. - setCurrentScreenBrightness(mPendingScreenBrightnessSetting); - updateAutoBrightnessAdjustment(); + mDisplayBrightnessController + .setAndNotifyCurrentScreenBrightness(mDisplayBrightnessController + .getPendingScreenBrightness()); if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.resetShortTermModel(); } @@ -2781,129 +2417,59 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); mHandler.postAtTime(() -> { - mUseAutoBrightness = screenBrightnessModeSetting - == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; + mAutomaticBrightnessStrategy.setUseAutoBrightness(screenBrightnessModeSetting + == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); updatePowerState(); }, mClock.uptimeMillis()); } - private float getAutoBrightnessAdjustmentSetting() { - final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT); - return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj); - } @Override public float getScreenBrightnessSetting() { - float brightness = mBrightnessSetting.getBrightness(); - if (Float.isNaN(brightness)) { - brightness = mScreenBrightnessDefault; - } - return clampAbsoluteBrightness(brightness); - } - - private void loadNitBasedBrightnessSetting() { - if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) { - float brightnessNitsForDefaultDisplay = - mBrightnessSetting.getBrightnessNitsForDefaultDisplay(); - if (brightnessNitsForDefaultDisplay >= 0) { - float brightnessForDefaultDisplay = getBrightnessFromNits( - brightnessNitsForDefaultDisplay); - if (isValidBrightnessValue(brightnessForDefaultDisplay)) { - mBrightnessSetting.setBrightness(brightnessForDefaultDisplay); - mCurrentScreenBrightnessSetting = brightnessForDefaultDisplay; - return; - } - } - } - mCurrentScreenBrightnessSetting = getScreenBrightnessSetting(); + return mDisplayBrightnessController.getScreenBrightnessSetting(); } @Override public void setBrightness(float brightnessValue, int userSerial) { - // Update the setting, which will eventually call back into DPC to have us actually update - // the display with the new value. - float clampedBrightnessValue = clampScreenBrightness(brightnessValue); - mBrightnessSetting.setUserSerial(userSerial); - mBrightnessSetting.setBrightness(clampedBrightnessValue); - if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) { - float nits = convertToNits(clampedBrightnessValue); - if (nits >= 0) { - mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits); - } - } + mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue), + userSerial); } @Override - public void onBootCompleted() { - Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - private void updateScreenBrightnessSetting(float brightnessValue) { - if (!isValidBrightnessValue(brightnessValue) - || brightnessValue == mCurrentScreenBrightnessSetting) { - return; - } - setCurrentScreenBrightness(brightnessValue); - setBrightness(brightnessValue); - } - - private void setCurrentScreenBrightness(float brightnessValue) { - if (brightnessValue != mCurrentScreenBrightnessSetting) { - mCurrentScreenBrightnessSetting = brightnessValue; - postBrightnessChangeRunnable(); - } - } - - private void putAutoBrightnessAdjustmentSetting(float adjustment) { - if (mDisplayId == Display.DEFAULT_DISPLAY) { - mAutoBrightnessAdjustment = adjustment; - Settings.System.putFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, - UserHandle.USER_CURRENT); - } + public int getDisplayId() { + return mDisplayId; } - private boolean updateAutoBrightnessAdjustment() { - if (Float.isNaN(mPendingAutoBrightnessAdjustment)) { - return false; - } - if (mAutoBrightnessAdjustment == mPendingAutoBrightnessAdjustment) { - mPendingAutoBrightnessAdjustment = Float.NaN; - return false; - } - mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment; - mPendingAutoBrightnessAdjustment = Float.NaN; - mTemporaryAutoBrightnessAdjustment = Float.NaN; - return true; + @Override + public int getLeadDisplayId() { + return mLeadDisplayId; } - // We want to return true if the user has set the screen brightness. - // RBC on, off, and intensity changes will return false. - // Slider interactions whilst in RBC will return true, just as when in non-rbc. - private boolean updateUserSetScreenBrightness() { - if ((Float.isNaN(mPendingScreenBrightnessSetting) - || mPendingScreenBrightnessSetting < 0.0f)) { - return false; - } - if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) { - mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - return false; + @Override + public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, + boolean slowChange) { + mBrightnessRangeController.onAmbientLuxChange(ambientLux); + if (nits == BrightnessMappingStrategy.INVALID_NITS) { + mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange); + } else { + float brightness = mDisplayBrightnessController.getBrightnessFromNits(nits); + if (BrightnessUtils.isValidBrightnessValue(brightness)) { + mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange); + } else { + // The device does not support nits + mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, + slowChange); + } } - setCurrentScreenBrightness(mPendingScreenBrightnessSetting); - mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting; - mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - return true; + sendUpdatePowerState(); } private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, boolean wasShortTermModelActive, boolean autobrightnessEnabled, - boolean brightnessIsTemporary) { - final float brightnessInNits = convertToAdjustedNits(brightness); + boolean brightnessIsTemporary, boolean shouldUseAutoBrightness) { + final float brightnessInNits = + mDisplayBrightnessController.convertToAdjustedNits(brightness); // Don't report brightness to brightnessTracker: // If brightness is temporary (ie the slider has not been released) // or if we are in idle screen brightness mode. @@ -2915,12 +2481,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call || mAutomaticBrightnessController.isInIdleMode() || !autobrightnessEnabled || mBrightnessTracker == null - || !mUseAutoBrightness + || !shouldUseAutoBrightness || brightnessInNits < 0.0f) { return; } - if (userInitiated && !mAutomaticBrightnessController.hasValidAmbientLux()) { + if (userInitiated && (mAutomaticBrightnessController == null + || !mAutomaticBrightnessController.hasValidAmbientLux())) { // If we don't have a valid lux reading we can't report a valid // slider event so notify as if the system changed the brightness. userInitiated = false; @@ -2939,96 +2506,33 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.getLastSensorTimestamps()); } - private float convertToNits(float brightness) { - if (mAutomaticBrightnessController == null) { - return BrightnessMappingStrategy.INVALID_NITS; - } - return mAutomaticBrightnessController.convertToNits(brightness); - } - - private float convertToAdjustedNits(float brightness) { - if (mAutomaticBrightnessController == null) { - return BrightnessMappingStrategy.INVALID_NITS; + @Override + public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { + synchronized (mLock) { + mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower); + sendUpdatePowerStateLocked(); } - return mAutomaticBrightnessController.convertToAdjustedNits(brightness); } - private float getBrightnessFromNits(float nits) { - if (mAutomaticBrightnessController == null) { - return PowerManager.BRIGHTNESS_INVALID_FLOAT; + @Override + public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { + synchronized (mLock) { + mDisplayBrightnessFollowers.remove(follower.getDisplayId()); + mHandler.postAtTime(() -> follower.setBrightnessToFollow( + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } - return mAutomaticBrightnessController.getBrightnessFromNits(nits); } @GuardedBy("mLock") - private void updatePendingProximityRequestsLocked() { - mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; - mPendingWaitForNegativeProximityLocked = false; - - if (mIgnoreProximityUntilChanged) { - // Also, lets stop waiting for negative proximity if we're ignoring it. - mWaitingForNegativeProximity = false; - } - } - - private void ignoreProximitySensorUntilChangedInternal() { - if (!mIgnoreProximityUntilChanged - && mProximity == PROXIMITY_POSITIVE) { - // Only ignore if it is still reporting positive (near) - mIgnoreProximityUntilChanged = true; - Slog.i(mTag, "Ignoring proximity"); - updatePowerState(); - } - } - - private final Runnable mOnStateChangedRunnable = new Runnable() { - @Override - public void run() { - mOnStateChangedPending = false; - mCallbacks.onStateChanged(); - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged); - } - }; - - private void sendOnProximityPositiveWithWakelock() { - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxPositive); - mHandler.post(mOnProximityPositiveRunnable); - mOnProximityPositiveMessages++; - } - - private final Runnable mOnProximityPositiveRunnable = new Runnable() { - @Override - public void run() { - mOnProximityPositiveMessages--; - mCallbacks.onProximityPositive(); - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive); - } - }; - - private void sendOnProximityNegativeWithWakelock() { - mOnProximityNegativeMessages++; - mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxNegative); - mHandler.post(mOnProximityNegativeRunnable); - } - - private final Runnable mOnProximityNegativeRunnable = new Runnable() { - @Override - public void run() { - mOnProximityNegativeMessages--; - mCallbacks.onProximityNegative(); - mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative); + private void clearDisplayBrightnessFollowersLocked() { + for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { + DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); + mHandler.postAtTime(() -> follower.setBrightnessToFollow( + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } - }; - - /** - * Indicates whether the display state is ready to update. If this is the default display, we - * want to update it right away so that we can draw the boot animation on it. If it is not - * the default display, drawing the boot animation on it would look incorrect, so we need - * to wait until boot is completed. - * @return True if the display state is ready to update - */ - private boolean readyToUpdateDisplayState() { - return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted; + mDisplayBrightnessFollowers.clear(); } @Override @@ -3040,31 +2544,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mLeadDisplayId=" + mLeadDisplayId); pw.println(" mLightSensor=" + mLightSensor); pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers); - pw.println(" mDozeStateOverride=" + mDozeStateOverride); pw.println(); pw.println("Display Power Controller Locked State:"); pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked); pw.println(" mPendingRequestLocked=" + mPendingRequestLocked); pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked); - pw.println(" mPendingWaitForNegativeProximityLocked=" - + mPendingWaitForNegativeProximityLocked); pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked); } pw.println(); pw.println("Display Power Controller Configuration:"); - pw.println(" mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault); pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); - pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); - pw.println(" mAllowAutoBrightnessWhileDozingConfig=" - + mAllowAutoBrightnessWhileDozingConfig); - pw.println(" mPersistBrightnessNitsForDefaultDisplay=" - + mPersistBrightnessNitsForDefaultDisplay); pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp); pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig); pw.println(" mColorFadeEnabled=" + mColorFadeEnabled); + pw.println(" mIsDisplayInternal=" + mIsDisplayInternal); synchronized (mCachedBrightnessInfo) { pw.println(" mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness.value); @@ -3082,7 +2578,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig); pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig); - mHandler.runWithScissors(() -> dumpLocal(pw), 1000); } @@ -3090,35 +2585,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(); pw.println("Display Power Controller Thread State:"); pw.println(" mPowerRequest=" + mPowerRequest); - pw.println(" mUnfinishedBusiness=" + mUnfinishedBusiness); - pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity); - pw.println(" mProximitySensor=" + mProximitySensor); - pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); - pw.println(" mProximityThreshold=" + mProximityThreshold); - pw.println(" mProximity=" + proximityToString(mProximity)); - pw.println(" mPendingProximity=" + proximityToString(mPendingProximity)); - pw.println(" mPendingProximityDebounceTime=" - + TimeUtils.formatUptime(mPendingProximityDebounceTime)); - pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity); - pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness); - pw.println(" mPendingScreenBrightnessSetting=" - + mPendingScreenBrightnessSetting); - pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness); - pw.println(" mBrightnessToFollow=" + mBrightnessToFollow); - pw.println(" mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange); - pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mBrightnessReason=" + mBrightnessReason); - pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment); - pw.println(" mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment); - pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness); pw.println(" mAppliedDimming=" + mAppliedDimming); - pw.println(" mAppliedLowPower=" + mAppliedLowPower); pw.println(" mAppliedThrottling=" + mAppliedThrottling); - pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride); - pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness); - pw.println(" mAppliedTemporaryAutoBrightnessAdjustment=" - + mAppliedTemporaryAutoBrightnessAdjustment); - pw.println(" mAppliedBrightnessBoost=" + mAppliedBrightnessBoost); pw.println(" mDozing=" + mDozing); pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState)); pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime); @@ -3129,9 +2598,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mReportedToPolicy=" + reportedToPolicyToString(mReportedScreenStateToPolicy)); pw.println(" mIsRbcActive=" + mIsRbcActive); - pw.println(" mOnStateChangePending=" + mOnStateChangedPending); - pw.println(" mOnProximityPositiveMessages=" + mOnProximityPositiveMessages); - pw.println(" mOnProximityNegativeMessages=" + mOnProximityNegativeMessages); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + mAutomaticBrightnessStrategy.dump(ipw); if (mScreenBrightnessRampAnimator != null) { pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" @@ -3156,6 +2624,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call dumpBrightnessEvents(pw); } + dumpRbcEvents(pw); + if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.dump(pw); } @@ -3173,21 +2643,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayWhiteBalanceController.dump(pw); mDisplayWhiteBalanceSettings.dump(pw); } - } - private static String proximityToString(int state) { - switch (state) { - case PROXIMITY_UNKNOWN: - return "Unknown"; - case PROXIMITY_NEGATIVE: - return "Negative"; - case PROXIMITY_POSITIVE: - return "Positive"; - default: - return Integer.toString(state); + pw.println(); + + if (mWakelockController != null) { + mWakelockController.dumpLocal(pw); + } + + pw.println(); + if (mDisplayBrightnessController != null) { + mDisplayBrightnessController.dump(pw); + } + + pw.println(); + if (mDisplayStateController != null) { + mDisplayStateController.dumpsys(pw); + } + + pw.println(); + if (mBrightnessClamperController != null) { + mBrightnessClamperController.dump(ipw); } } + private static String reportedToPolicyToString(int state) { switch (state) { case REPORTED_TO_POLICY_SCREEN_OFF: @@ -3228,15 +2707,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private static float clampAbsoluteBrightness(float value) { - return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX); - } + private void dumpRbcEvents(PrintWriter pw) { + int size = mRbcEventRingBuffer.size(); + if (size < 1) { + pw.println("No Reduce Bright Colors Adjustments"); + return; + } - private static float clampAutoBrightnessAdjustment(float value) { - return MathUtils.constrain(value, -1.0f, 1.0f); + pw.println("Reduce Bright Colors Adjustments Last " + size + " Events: "); + BrightnessEvent[] eventArray = mRbcEventRingBuffer.toArray(); + for (int i = 0; i < mRbcEventRingBuffer.size(); i++) { + pw.println(" " + eventArray[i]); + } } + private void noteScreenState(int screenState) { // Log screen state change with display id FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED_V2, @@ -3363,20 +2848,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // It's easier to check if the brightness is at maximum level using the brightness // value untouched by any modifiers boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax(); - float brightnessInNits = convertToAdjustedNits(event.getBrightness()); + float brightnessInNits = + mDisplayBrightnessController.convertToAdjustedNits(event.getBrightness()); float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f; int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1; float appliedHbmMaxNits = event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF - ? -1f : convertToAdjustedNits(event.getHbmMax()); + ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getHbmMax()); // thermalCapNits set to -1 if not currently capping max brightness float appliedThermalCapNits = event.getThermalMax() == PowerManager.BRIGHTNESS_MAX - ? -1f : convertToAdjustedNits(event.getThermalMax()); - + ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getThermalMax()); if (mIsDisplayInternal) { FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED, - convertToAdjustedNits(event.getInitialBrightness()), + mDisplayBrightnessController + .convertToAdjustedNits(event.getInitialBrightness()), brightnessInNits, event.getLux(), event.getPhysicalDisplayId(), @@ -3393,7 +2879,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0, - mBrightnessThrottler.getBrightnessMaxReason(), + mBrightnessClamperController.getBrightnessMaxReason(), + // TODO: (flc) add brightnessMinReason here too. (modifier & BrightnessReason.MODIFIER_DIMMED) > 0, event.isRbcEnabled(), (flags & BrightnessEvent.FLAG_INVALID_LUX) > 0, @@ -3404,6 +2891,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + /** + * Indicates whether the display state is ready to update. If this is the default display, we + * want to update it right away so that we can draw the boot animation on it. If it is not + * the default display, drawing the boot animation on it would look incorrect, so we need + * to wait until boot is completed. + * @return True if the display state is ready to update + */ + private boolean readyToUpdateDisplayState() { + return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted; + } + private final class DisplayControllerHandler extends Handler { DisplayControllerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -3416,10 +2914,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updatePowerState(); break; - case MSG_PROXIMITY_SENSOR_DEBOUNCED: - debounceProximitySensor(); - break; - case MSG_SCREEN_ON_UNBLOCKED: if (mPendingScreenOnUnblocker == msg.obj) { unblockScreenOn(); @@ -3432,27 +2926,38 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updatePowerState(); } break; + case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED: + if (mDisplayOffloadSession == msg.obj) { + unblockScreenOnByDisplayOffload(); + updatePowerState(); + } + break; case MSG_CONFIGURE_BRIGHTNESS: - mBrightnessConfiguration = (BrightnessConfiguration) msg.obj; - mShouldResetShortTermModel = msg.arg1 == 1; + BrightnessConfiguration brightnessConfiguration = + (BrightnessConfiguration) msg.obj; + mAutomaticBrightnessStrategy.setBrightnessConfiguration(brightnessConfiguration, + msg.arg1 == 1); + if (mBrightnessTracker != null) { + mBrightnessTracker + .setShouldCollectColorSample(brightnessConfiguration != null + && brightnessConfiguration.shouldCollectColorSamples()); + } updatePowerState(); break; case MSG_SET_TEMPORARY_BRIGHTNESS: // TODO: Should we have a a timeout for the temporary brightness? - mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1); + mDisplayBrightnessController + .setTemporaryBrightness(Float.intBitsToFloat(msg.arg1)); updatePowerState(); break; case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT: - mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1); + mAutomaticBrightnessStrategy + .setTemporaryAutoBrightnessAdjustment(Float.intBitsToFloat(msg.arg1)); updatePowerState(); break; - case MSG_IGNORE_PROXIMITY: - ignoreProximitySensorUntilChangedInternal(); - break; - case MSG_STOP: cleanupHandlerThreadAfterStop(); break; @@ -3500,27 +3005,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call case MSG_SET_DWBC_LOGGING_ENABLED: setDwbcLoggingEnabled(msg.arg1); break; + case MSG_SET_BRIGHTNESS_FROM_OFFLOAD: + mDisplayBrightnessController.setBrightnessFromOffload( + Float.intBitsToFloat(msg.arg1)); + updatePowerState(); + break; } } } - private final SensorEventListener mProximitySensorListener = new SensorEventListener() { - @Override - public void onSensorChanged(SensorEvent event) { - if (mProximitySensorEnabled) { - final long time = mClock.uptimeMillis(); - final float distance = event.values[0]; - boolean positive = distance >= 0.0f && distance < mProximityThreshold; - handleProximitySensorEvent(time, positive); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // Not used. - } - }; - private final class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { @@ -3531,6 +3024,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public void onChange(boolean selfChange, Uri uri) { if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) { handleBrightnessModeChange(); + } else if (uri.equals(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) { + int preset = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, + UserHandle.USER_CURRENT); + Slog.i(mTag, "Setting up auto-brightness for preset " + + autoBrightnessPresetToString(preset)); + setUpAutoBrightness(mContext, mHandler); + sendUpdatePowerState(); } else { handleSettingsChange(false /* userSwitch */); } @@ -3581,28 +3084,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call msg.sendToTarget(); } - @VisibleForTesting - String getSuspendBlockerUnfinishedBusinessId(int displayId) { - return "[" + displayId + "]unfinished business"; - } - - String getSuspendBlockerOnStateChangedId(int displayId) { - return "[" + displayId + "]on state changed"; - } - - String getSuspendBlockerProxPositiveId(int displayId) { - return "[" + displayId + "]prox positive"; - } - - String getSuspendBlockerProxNegativeId(int displayId) { - return "[" + displayId + "]prox negative"; - } - - @VisibleForTesting - String getSuspendBlockerProxDebounceId(int displayId) { - return "[" + displayId + "]prox debounce"; - } - /** Functional interface for providing time. */ @VisibleForTesting interface Clock { @@ -3629,6 +3110,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return new DualRampAnimator(dps, firstProperty, secondProperty); } + WakelockController getWakelockController(int displayId, + DisplayPowerCallbacks displayPowerCallbacks) { + return new WakelockController(displayId, displayPowerCallbacks); + } + + DisplayPowerProximityStateController getDisplayPowerProximityStateController( + WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, + Looper looper, Runnable nudgeUpdatePowerState, + int displayId, SensorManager sensorManager) { + return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig, + looper, nudgeUpdatePowerState, + displayId, sensorManager, /* injector= */ null); + } + AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, @@ -3711,11 +3206,32 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call hbmChangeCallback, hbmMetadata, context); } + BrightnessRangeController getBrightnessRangeController( + HighBrightnessModeController hbmController, Runnable modeChangeCallback, + DisplayDeviceConfig displayDeviceConfig, Handler handler, + DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { + return new BrightnessRangeController(hbmController, + modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info); + } + + BrightnessClamperController getBrightnessClamperController(Handler handler, + BrightnessClamperController.ClamperChangeListener clamperChangeListener, + BrightnessClamperController.DisplayDeviceData data, Context context, + DisplayManagerFlags flags) { + + return new BrightnessClamperController(handler, clamperChangeListener, data, context, + flags); + } + DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, SensorManager sensorManager, Resources resources) { return DisplayWhiteBalanceFactory.create(handler, sensorManager, resources); } + + boolean isColorFadeEnabled() { + return !ActivityManager.isLowRamDeviceStatic(); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java deleted file mode 100644 index 2d860c0cc673..000000000000 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ /dev/null @@ -1,3267 +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.android.server.display; - -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; -import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString; - -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.annotation.UserIdInt; -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ParceledListSlice; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import android.hardware.display.AmbientBrightnessDayStats; -import android.hardware.display.BrightnessChangeEvent; -import android.hardware.display.BrightnessConfiguration; -import android.hardware.display.BrightnessInfo; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession; -import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; -import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; -import android.metrics.LogMaker; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.Trace; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.FloatProperty; -import android.util.IndentingPrintWriter; -import android.util.MathUtils; -import android.util.MutableFloat; -import android.util.MutableInt; -import android.util.Slog; -import android.util.SparseArray; -import android.view.Display; - -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.IBatteryStats; -import com.android.internal.display.BrightnessSynchronizer; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.RingBuffer; -import com.android.server.LocalServices; -import com.android.server.am.BatteryStatsService; -import com.android.server.display.RampAnimator.DualRampAnimator; -import com.android.server.display.brightness.BrightnessEvent; -import com.android.server.display.brightness.BrightnessReason; -import com.android.server.display.brightness.BrightnessUtils; -import com.android.server.display.brightness.DisplayBrightnessController; -import com.android.server.display.brightness.clamper.BrightnessClamperController; -import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; -import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; -import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; -import com.android.server.display.feature.DisplayManagerFlags; -import com.android.server.display.layout.Layout; -import com.android.server.display.state.DisplayStateController; -import com.android.server.display.utils.DebugUtils; -import com.android.server.display.utils.SensorUtils; -import com.android.server.display.whitebalance.DisplayWhiteBalanceController; -import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; -import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings; -import com.android.server.policy.WindowManagerPolicy; - -import java.io.PrintWriter; -import java.util.Objects; - -/** - * Controls the power state of the display. - * - * Handles the proximity sensor, light sensor, and animations between states - * including the screen off animation. - * - * This component acts independently of the rest of the power manager service. - * In particular, it does not share any state and it only communicates - * via asynchronous callbacks to inform the power manager that something has - * changed. - * - * Everything this class does internally is serialized on its handler although - * it may be accessed by other threads from the outside. - * - * Note that the power manager service guarantees that it will hold a suspend - * blocker as long as the display is not ready. So most of the work done here - * does not need to worry about holding a suspend blocker unless it happens - * independently of the display ready signal. - * - * For debugging, you can make the color fade and brightness animations run - * slower by changing the "animator duration scale" option in Development Settings. - */ -final class DisplayPowerController2 implements AutomaticBrightnessController.Callbacks, - DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface { - private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked"; - private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked"; - - private static final String TAG = "DisplayPowerController2"; - // To enable these logs, run: - // 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot' - private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); - private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME = - "Screen on blocked by displayoffload"; - - // If true, uses the color fade on animation. - // We might want to turn this off if we cannot get a guarantee that the screen - // actually turns on and starts showing new content after the call to set the - // screen state returns. Playing the animation can also be somewhat slow. - private static final boolean USE_COLOR_FADE_ON_ANIMATION = false; - - private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f; - - private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250; - private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400; - - private static final int MSG_UPDATE_POWER_STATE = 1; - private static final int MSG_SCREEN_ON_UNBLOCKED = 2; - private static final int MSG_SCREEN_OFF_UNBLOCKED = 3; - private static final int MSG_CONFIGURE_BRIGHTNESS = 4; - private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 5; - private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 6; - private static final int MSG_STOP = 7; - private static final int MSG_UPDATE_BRIGHTNESS = 8; - private static final int MSG_UPDATE_RBC = 9; - private static final int MSG_BRIGHTNESS_RAMP_DONE = 10; - private static final int MSG_STATSD_HBM_BRIGHTNESS = 11; - private static final int MSG_SWITCH_USER = 12; - private static final int MSG_BOOT_COMPLETED = 13; - private static final int MSG_SET_DWBC_STRONG_MODE = 14; - private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15; - private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16; - private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17; - private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18; - - - - private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500; - - - // State machine constants for tracking initial brightness ramp skipping when enabled. - private static final int RAMP_STATE_SKIP_NONE = 0; - private static final int RAMP_STATE_SKIP_INITIAL = 1; - private static final int RAMP_STATE_SKIP_AUTOBRIGHT = 2; - - private static final int REPORTED_TO_POLICY_UNREPORTED = -1; - private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0; - private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1; - private static final int REPORTED_TO_POLICY_SCREEN_ON = 2; - private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3; - - private static final int RINGBUFFER_MAX = 100; - private static final int RINGBUFFER_RBC_MAX = 20; - - private static final float[] BRIGHTNESS_RANGE_BOUNDARIES = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, - 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200, - 1400, 1600, 1800, 2000, 2250, 2500, 2750, 3000}; - private static final int[] BRIGHTNESS_RANGE_INDEX = { - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_UNKNOWN, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_0_1, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1_2, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2_3, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_3_4, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_4_5, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_5_6, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_6_7, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_7_8, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_8_9, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_9_10, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_10_20, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_20_30, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_30_40, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_40_50, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_50_60, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_60_70, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_70_80, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_80_90, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_90_100, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_100_200, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_200_300, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_300_400, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_400_500, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_500_600, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_600_700, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_700_800, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_800_900, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_900_1000, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1000_1200, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1200_1400, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1400_1600, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1600_1800, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_1800_2000, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2000_2250, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2250_2500, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2500_2750, - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_2750_3000, - }; - - private final String mTag; - - private final Object mLock = new Object(); - - private final Context mContext; - - // Our handler. - private final DisplayControllerHandler mHandler; - - // Battery stats. - @Nullable - private final IBatteryStats mBatteryStats; - - // The sensor manager. - private final SensorManager mSensorManager; - - // The window manager policy. - private final WindowManagerPolicy mWindowManagerPolicy; - - // The display blanker. - private final DisplayBlanker mBlanker; - - // The LogicalDisplay tied to this DisplayPowerController2. - private final LogicalDisplay mLogicalDisplay; - - // The ID of the LogicalDisplay tied to this DisplayPowerController2. - private final int mDisplayId; - - // The ID of the display which this display follows for brightness purposes. - private int mLeadDisplayId = Layout.NO_LEAD_DISPLAY; - - // The unique ID of the primary display device currently tied to this logical display - private String mUniqueDisplayId; - - // Tracker for brightness changes. - @Nullable - private final BrightnessTracker mBrightnessTracker; - - // Tracker for brightness settings changes. - private final SettingsObserver mSettingsObserver; - - // The doze screen brightness. - private final float mScreenBrightnessDozeConfig; - - // True if auto-brightness should be used. - private boolean mUseSoftwareAutoBrightnessConfig; - - // Whether or not the color fade on screen on / off is enabled. - private final boolean mColorFadeEnabled; - - @GuardedBy("mCachedBrightnessInfo") - private final CachedBrightnessInfo mCachedBrightnessInfo = new CachedBrightnessInfo(); - - private DisplayDevice mDisplayDevice; - - // True if we should fade the screen while turning it off, false if we should play - // a stylish color fade animation instead. - private final boolean mColorFadeFadesConfig; - - // True if we need to fake a transition to off when coming out of a doze state. - // Some display hardware will blank itself when coming out of doze in order to hide - // artifacts. For these displays we fake a transition into OFF so that policy can appropriately - // blank itself and begin an appropriate power on animation. - private final boolean mDisplayBlanksAfterDozeConfig; - - // True if there are only buckets of brightness values when the display is in the doze state, - // rather than a full range of values. If this is true, then we'll avoid animating the screen - // brightness since it'd likely be multiple jarring brightness transitions instead of just one - // to reach the final state. - private final boolean mBrightnessBucketsInDozeConfig; - - private final Clock mClock; - private final Injector mInjector; - - // Maximum time a ramp animation can take. - private long mBrightnessRampIncreaseMaxTimeMillis; - private long mBrightnessRampDecreaseMaxTimeMillis; - - // Maximum time a ramp animation can take in idle mode. - private long mBrightnessRampIncreaseMaxTimeIdleMillis; - private long mBrightnessRampDecreaseMaxTimeIdleMillis; - - // The pending power request. - // Initially null until the first call to requestPowerState. - @GuardedBy("mLock") - private DisplayPowerRequest mPendingRequestLocked; - - // True if the pending power request or wait for negative proximity flag - // has been changed since the last update occurred. - @GuardedBy("mLock") - private boolean mPendingRequestChangedLocked; - - // Set to true when the important parts of the pending power request have been applied. - // The important parts are mainly the screen state. Brightness changes may occur - // concurrently. - @GuardedBy("mLock") - private boolean mDisplayReadyLocked; - - // Set to true if a power state update is required. - @GuardedBy("mLock") - private boolean mPendingUpdatePowerStateLocked; - - /* The following state must only be accessed by the handler thread. */ - - // The currently requested power state. - // The power controller will progressively update its internal state to match - // the requested power state. Initially null until the first update. - private DisplayPowerRequest mPowerRequest; - - // The current power state. - // Must only be accessed on the handler thread. - private DisplayPowerState mPowerState; - - - - // The currently active screen on unblocker. This field is non-null whenever - // we are waiting for a callback to release it and unblock the screen. - private ScreenOnUnblocker mPendingScreenOnUnblocker; - private ScreenOffUnblocker mPendingScreenOffUnblocker; - private Runnable mPendingScreenOnUnblockerByDisplayOffload; - - // True if we were in the process of turning off the screen. - // This allows us to recover more gracefully from situations where we abort - // turning off the screen. - private boolean mPendingScreenOff; - - // The elapsed real time when the screen on was blocked. - private long mScreenOnBlockStartRealTime; - private long mScreenOffBlockStartRealTime; - private long mScreenOnBlockByDisplayOffloadStartRealTime; - - // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields. - private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED; - - // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on. - // This value is reset when screen on is reported or the blocking is cancelled. - private boolean mScreenTurningOnWasBlockedByDisplayOffload; - - // If the last recorded screen state was dozing or not. - private boolean mDozing; - - private boolean mAppliedDimming; - - private boolean mAppliedThrottling; - - // Reason for which the brightness was last changed. See {@link BrightnessReason} for more - // information. - // At the time of this writing, this value is changed within updatePowerState() only, which is - // limited to the thread used by DisplayControllerHandler. - private final BrightnessReason mBrightnessReason = new BrightnessReason(); - private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason(); - - // Brightness animation ramp rates in brightness units per second - private float mBrightnessRampRateFastDecrease; - private float mBrightnessRampRateFastIncrease; - private float mBrightnessRampRateSlowDecrease; - private float mBrightnessRampRateSlowIncrease; - private float mBrightnessRampRateSlowDecreaseIdle; - private float mBrightnessRampRateSlowIncreaseIdle; - - // Report HBM brightness change to StatsD - private int mDisplayStatsId; - private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN; - - // Whether or not to skip the initial brightness ramps into STATE_ON. - private final boolean mSkipScreenOnBrightnessRamp; - - // Display white balance components. - // Critical methods must be called on DPC2 handler thread. - @Nullable - private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings; - @Nullable - private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; - - @Nullable - private final ColorDisplayServiceInternal mCdsi; - private float[] mNitsRange; - - private final BrightnessRangeController mBrightnessRangeController; - - private final BrightnessThrottler mBrightnessThrottler; - - private final BrightnessClamperController mBrightnessClamperController; - - private final Runnable mOnBrightnessChangeRunnable; - - private final BrightnessEvent mLastBrightnessEvent; - private final BrightnessEvent mTempBrightnessEvent; - - private final DisplayBrightnessController mDisplayBrightnessController; - - // Keeps a record of brightness changes for dumpsys. - private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer; - - // Keeps a record of rbc changes for dumpsys. - private final RingBuffer<BrightnessEvent> mRbcEventRingBuffer = - new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_RBC_MAX); - - // Controls and tracks all the wakelocks that are acquired/released by the system. Also acts as - // a medium of communication between this class and the PowerManagerService. - private final WakelockController mWakelockController; - - // Tracks and manages the proximity state of the associated display. - private final DisplayPowerProximityStateController mDisplayPowerProximityStateController; - - // Tracks and manages the display state of the associated display. - private final DisplayStateController mDisplayStateController; - - - // Responsible for evaluating and tracking the automatic brightness relevant states. - // Todo: This is a temporary workaround. Ideally DPC2 should never talk to the strategies - private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy; - - // A record of state for skipping brightness ramps. - private int mSkipRampState = RAMP_STATE_SKIP_NONE; - - // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL. - private float mInitialAutoBrightness; - - // The controller for the automatic brightness level. - @Nullable - private AutomaticBrightnessController mAutomaticBrightnessController; - - // The controller for the sensor used to estimate ambient lux while the display is off. - @Nullable - private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; - - private Sensor mLightSensor; - private Sensor mScreenOffBrightnessSensor; - - private boolean mIsRbcActive; - - // Animators. - private ObjectAnimator mColorFadeOnAnimator; - private ObjectAnimator mColorFadeOffAnimator; - private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; - - // True if this DisplayPowerController2 has been stopped and should no longer be running. - private boolean mStopped; - - private DisplayDeviceConfig mDisplayDeviceConfig; - - private boolean mIsEnabled; - private boolean mIsInTransition; - private boolean mIsDisplayInternal; - - // The id of the thermal brightness throttling policy that should be used. - private String mThermalBrightnessThrottlingDataId; - - // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there - // is one lead display, the additional displays follow the brightness value of the lead display. - @GuardedBy("mLock") - private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers = - new SparseArray(); - - private boolean mBootCompleted; - private final DisplayManagerFlags mFlags; - - private DisplayOffloadSession mDisplayOffloadSession; - - /** - * Creates the display power controller. - */ - DisplayPowerController2(Context context, Injector injector, - DisplayPowerCallbacks callbacks, Handler handler, - SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay, - BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting, - Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata, - boolean bootCompleted, DisplayManagerFlags flags) { - mFlags = flags; - mInjector = injector != null ? injector : new Injector(); - mClock = mInjector.getClock(); - mLogicalDisplay = logicalDisplay; - mDisplayId = mLogicalDisplay.getDisplayIdLocked(); - mSensorManager = sensorManager; - mHandler = new DisplayControllerHandler(handler.getLooper()); - mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceConfig(); - mIsEnabled = logicalDisplay.isEnabledLocked(); - mIsInTransition = logicalDisplay.isInTransitionLocked(); - mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; - mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks); - mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController( - mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(), - () -> updatePowerState(), mDisplayId, mSensorManager); - mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController); - mTag = TAG + "[" + mDisplayId + "]"; - mThermalBrightnessThrottlingDataId = - logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; - mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); - mDisplayStatsId = mUniqueDisplayId.hashCode(); - - mLastBrightnessEvent = new BrightnessEvent(mDisplayId); - mTempBrightnessEvent = new BrightnessEvent(mDisplayId); - - if (mDisplayId == Display.DEFAULT_DISPLAY) { - mBatteryStats = BatteryStatsService.getService(); - } else { - mBatteryStats = null; - } - - mSettingsObserver = new SettingsObserver(mHandler); - mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class); - mBlanker = blanker; - mContext = context; - mBrightnessTracker = brightnessTracker; - mOnBrightnessChangeRunnable = onBrightnessChangeRunnable; - - PowerManager pm = context.getSystemService(PowerManager.class); - - final Resources resources = context.getResources(); - - // DOZE AND DIM SETTINGS - mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)); - loadBrightnessRampRates(); - mSkipScreenOnBrightnessRamp = resources.getBoolean( - R.bool.config_skipScreenOnBrightnessRamp); - - Runnable modeChangeCallback = () -> { - sendUpdatePowerState(); - postBrightnessChangeRunnable(); - // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern. - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.update(); - } - }; - - HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata, - modeChangeCallback); - mBrightnessThrottler = createBrightnessThrottlerLocked(); - - mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController, - modeChangeCallback, mDisplayDeviceConfig, mHandler, flags, - mDisplayDevice.getDisplayTokenLocked(), - mDisplayDevice.getDisplayDeviceInfoLocked()); - - mDisplayBrightnessController = - new DisplayBrightnessController(context, null, - mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, - brightnessSetting, () -> postBrightnessChangeRunnable(), - new HandlerExecutor(mHandler), flags); - - mBrightnessClamperController = mInjector.getBrightnessClamperController( - mHandler, modeChangeCallback::run, - new BrightnessClamperController.DisplayDeviceData( - mUniqueDisplayId, - mThermalBrightnessThrottlingDataId, - logicalDisplay.getPowerThrottlingDataIdLocked(), - mDisplayDeviceConfig), mContext, flags); - // Seed the cached brightness - saveBrightnessInfo(getScreenBrightnessSetting()); - mAutomaticBrightnessStrategy = - mDisplayBrightnessController.getAutomaticBrightnessStrategy(); - - DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; - DisplayWhiteBalanceController displayWhiteBalanceController = null; - if (mDisplayId == Display.DEFAULT_DISPLAY) { - try { - displayWhiteBalanceController = mInjector.getDisplayWhiteBalanceController( - mHandler, mSensorManager, resources); - displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); - displayWhiteBalanceSettings.setCallbacks(this); - displayWhiteBalanceController.setCallbacks(this); - } catch (Exception e) { - Slog.e(mTag, "failed to set up display white-balance: " + e); - } - } - mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings; - mDisplayWhiteBalanceController = displayWhiteBalanceController; - - loadNitsRange(resources); - - if (mDisplayId == Display.DEFAULT_DISPLAY) { - mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); - if (mCdsi != null) { - boolean active = mCdsi.setReduceBrightColorsListener( - new ReduceBrightColorsListener() { - @Override - public void onReduceBrightColorsActivationChanged(boolean activated, - boolean userInitiated) { - applyReduceBrightColorsSplineAdjustment(); - - } - - @Override - public void onReduceBrightColorsStrengthChanged(int strength) { - applyReduceBrightColorsSplineAdjustment(); - } - }); - if (active) { - applyReduceBrightColorsSplineAdjustment(); - } - } - } else { - mCdsi = null; - } - - setUpAutoBrightness(context, handler); - - mColorFadeEnabled = mInjector.isColorFadeEnabled() - && !resources.getBoolean( - com.android.internal.R.bool.config_displayColorFadeDisabled); - mColorFadeFadesConfig = resources.getBoolean( - R.bool.config_animateScreenLights); - - mDisplayBlanksAfterDozeConfig = resources.getBoolean( - R.bool.config_displayBlanksAfterDoze); - - mBrightnessBucketsInDozeConfig = resources.getBoolean( - R.bool.config_displayBrightnessBucketsInDoze); - - mBootCompleted = bootCompleted; - } - - private void applyReduceBrightColorsSplineAdjustment() { - mHandler.obtainMessage(MSG_UPDATE_RBC).sendToTarget(); - sendUpdatePowerState(); - } - - private void handleRbcChanged() { - if (mAutomaticBrightnessController == null) { - return; - } - - float[] adjustedNits = new float[mNitsRange.length]; - for (int i = 0; i < mNitsRange.length; i++) { - adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]); - } - mIsRbcActive = mCdsi.isReduceBrightColorsActivated(); - mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits); - } - - /** - * Returns true if the proximity sensor screen-off function is available. - */ - @Override - public boolean isProximitySensorAvailable() { - return mDisplayPowerProximityStateController.isProximitySensorAvailable(); - } - - /** - * Get the {@link BrightnessChangeEvent}s for the specified user. - * - * @param userId userId to fetch data for - * @param includePackage if false will null out the package name in events - */ - @Nullable - @Override - public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents( - @UserIdInt int userId, boolean includePackage) { - if (mBrightnessTracker == null) { - return null; - } - return mBrightnessTracker.getEvents(userId, includePackage); - } - - @Override - public void onSwitchUser(@UserIdInt int newUserId) { - Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId); - mHandler.sendMessage(msg); - } - - private void handleOnSwitchUser(@UserIdInt int newUserId) { - handleSettingsChange(true /* userSwitch */); - handleBrightnessModeChange(); - if (mBrightnessTracker != null) { - mBrightnessTracker.onSwitchUser(newUserId); - } - } - - @Nullable - @Override - public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats( - @UserIdInt int userId) { - if (mBrightnessTracker == null) { - return null; - } - return mBrightnessTracker.getAmbientBrightnessStats(userId); - } - - /** - * Persist the brightness slider events and ambient brightness stats to disk. - */ - @Override - public void persistBrightnessTrackerState() { - if (mBrightnessTracker != null) { - mBrightnessTracker.persistBrightnessTrackerState(); - } - } - - /** - * Requests a new power state. - * The controller makes a copy of the provided object and then - * begins adjusting the power state to match what was requested. - * - * @param request The requested power state. - * @param waitForNegativeProximity If true, issues a request to wait for - * negative proximity before turning the screen back on, - * assuming the screen - * was turned off by the proximity sensor. - * @return True if display is ready, false if there are important changes that must - * be made asynchronously (such as turning the screen on), in which case the caller - * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()} - * then try the request again later until the state converges. - */ - public boolean requestPowerState(DisplayPowerRequest request, - boolean waitForNegativeProximity) { - if (DEBUG) { - Slog.d(mTag, "requestPowerState: " - + request + ", waitForNegativeProximity=" + waitForNegativeProximity); - } - - synchronized (mLock) { - if (mStopped) { - return true; - } - - boolean changed = mDisplayPowerProximityStateController - .setPendingWaitForNegativeProximityLocked(waitForNegativeProximity); - - if (mPendingRequestLocked == null) { - mPendingRequestLocked = new DisplayPowerRequest(request); - changed = true; - } else if (!mPendingRequestLocked.equals(request)) { - mPendingRequestLocked.copyFrom(request); - changed = true; - } - - if (changed) { - mDisplayReadyLocked = false; - if (!mPendingRequestChangedLocked) { - mPendingRequestChangedLocked = true; - sendUpdatePowerStateLocked(); - } - } - - return mDisplayReadyLocked; - } - } - - @Override - public void overrideDozeScreenState(int displayState) { - mHandler.postAtTime(() -> { - if (mDisplayOffloadSession == null - || !(DisplayOffloadSession.isSupportedOffloadState(displayState) - || displayState == Display.STATE_UNKNOWN)) { - return; - } - mDisplayStateController.overrideDozeScreenState(displayState); - sendUpdatePowerState(); - }, mClock.uptimeMillis()); - } - - @Override - public void setDisplayOffloadSession(DisplayOffloadSession session) { - if (session == mDisplayOffloadSession) { - return; - } - unblockScreenOnByDisplayOffload(); - mDisplayOffloadSession = session; - } - - @Override - public BrightnessConfiguration getDefaultBrightnessConfiguration() { - if (mAutomaticBrightnessController == null) { - return null; - } - return mAutomaticBrightnessController.getDefaultConfig(); - } - - /** - * Notified when the display is changed. We use this to apply any changes that might be needed - * when displays get swapped on foldable devices. For example, different brightness properties - * of each display need to be properly reflected in AutomaticBrightnessController. - * - * Make sure DisplayManagerService.mSyncRoot lock is held when this is called - */ - @Override - public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) { - mLeadDisplayId = leadDisplayId; - final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - if (device == null) { - Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: " - + mLogicalDisplay.getDisplayIdLocked()); - return; - } - - final String uniqueId = device.getUniqueId(); - final DisplayDeviceConfig config = device.getDisplayDeviceConfig(); - final IBinder token = device.getDisplayTokenLocked(); - final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); - final boolean isEnabled = mLogicalDisplay.isEnabledLocked(); - final boolean isInTransition = mLogicalDisplay.isInTransitionLocked(); - final boolean isDisplayInternal = mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null - && mLogicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; - final String thermalBrightnessThrottlingDataId = - mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; - final String powerThrottlingDataId = - mLogicalDisplay.getPowerThrottlingDataIdLocked(); - - mHandler.postAtTime(() -> { - boolean changed = false; - if (mDisplayDevice != device) { - changed = true; - mDisplayDevice = device; - mUniqueDisplayId = uniqueId; - mDisplayStatsId = mUniqueDisplayId.hashCode(); - mDisplayDeviceConfig = config; - mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; - loadFromDisplayDeviceConfig(token, info, hbmMetadata); - mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config); - - // Since the underlying display-device changed, we really don't know the - // last command that was sent to change it's state. Let's assume it is unknown so - // that we trigger a change immediately. - mPowerState.resetScreenState(); - } else if (!Objects.equals(mThermalBrightnessThrottlingDataId, - thermalBrightnessThrottlingDataId)) { - changed = true; - mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; - mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( - config.getThermalBrightnessThrottlingDataMapByThrottlingId(), - mThermalBrightnessThrottlingDataId, - mUniqueDisplayId); - } - if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) { - changed = true; - mIsEnabled = isEnabled; - mIsInTransition = isInTransition; - } - - mIsDisplayInternal = isDisplayInternal; - // using local variables here, when mBrightnessThrottler is removed, - // mThermalBrightnessThrottlingDataId could be removed as well - // changed = true will be not needed - clampers are maintaining their state and - // will call updatePowerState if needed. - mBrightnessClamperController.onDisplayChanged( - new BrightnessClamperController.DisplayDeviceData(uniqueId, - thermalBrightnessThrottlingDataId, powerThrottlingDataId, config)); - - if (changed) { - updatePowerState(); - } - }, mClock.uptimeMillis()); - } - - /** - * Unregisters all listeners and interrupts all running threads; halting future work. - * - * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when - * the {@link #mDisplayId display} has been removed. - */ - @Override - public void stop() { - synchronized (mLock) { - clearDisplayBrightnessFollowersLocked(); - - mStopped = true; - Message msg = mHandler.obtainMessage(MSG_STOP); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.stop(); - } - - mDisplayBrightnessController.stop(); - - mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); - } - } - - private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info, - HighBrightnessModeMetadata hbmMetadata) { - // All properties that depend on the associated DisplayDevice and the DDC must be - // updated here. - loadBrightnessRampRates(); - loadNitsRange(mContext.getResources()); - setUpAutoBrightness(mContext, mHandler); - reloadReduceBrightColours(); - setAnimatorRampSpeeds(/* isIdleMode= */ false); - - mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig); - mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( - mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(), - mThermalBrightnessThrottlingDataId, mUniqueDisplayId); - } - - private void sendUpdatePowerState() { - synchronized (mLock) { - sendUpdatePowerStateLocked(); - } - } - - @GuardedBy("mLock") - private void sendUpdatePowerStateLocked() { - if (!mStopped && !mPendingUpdatePowerStateLocked) { - mPendingUpdatePowerStateLocked = true; - Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - } - - private void initialize(int displayState) { - mPowerState = mInjector.getDisplayPowerState(mBlanker, - mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState); - - if (mColorFadeEnabled) { - mColorFadeOnAnimator = ObjectAnimator.ofFloat( - mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f); - mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS); - mColorFadeOnAnimator.addListener(mAnimatorListener); - - mColorFadeOffAnimator = ObjectAnimator.ofFloat( - mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f); - mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS); - mColorFadeOffAnimator.addListener(mAnimatorListener); - } - - mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState, - DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, - DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT); - setAnimatorRampSpeeds(mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode()); - mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); - - noteScreenState(mPowerState.getScreenState()); - noteScreenBrightness(mPowerState.getScreenBrightness()); - - // Initialize all of the brightness tracking state - final float brightness = mDisplayBrightnessController.convertToAdjustedNits( - mPowerState.getScreenBrightness()); - if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) { - mBrightnessTracker.start(brightness); - } - - BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> { - Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - }; - mDisplayBrightnessController - .registerBrightnessSettingChangeListener(brightnessSettingListener); - - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), - false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), - false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); - if (mFlags.areAutoBrightnessModesEnabled()) { - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS), - /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT); - } - handleBrightnessModeChange(); - } - - private void setUpAutoBrightness(Context context, Handler handler) { - mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable(); - - if (!mUseSoftwareAutoBrightnessConfig) { - return; - } - - SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>(); - - BrightnessMappingStrategy defaultModeBrightnessMapper = - mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig, - mDisplayWhiteBalanceController); - brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT, - defaultModeBrightnessMapper); - - final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean( - R.bool.config_enableIdleScreenBrightnessMode); - if (isIdleScreenBrightnessEnabled) { - BrightnessMappingStrategy idleModeBrightnessMapper = - BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, - AUTO_BRIGHTNESS_MODE_IDLE, - mDisplayWhiteBalanceController); - if (idleModeBrightnessMapper != null) { - brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, - idleModeBrightnessMapper); - } - } - - BrightnessMappingStrategy dozeModeBrightnessMapper = - BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, - AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController); - if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) { - brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper); - } - - float userLux = BrightnessMappingStrategy.INVALID_LUX; - float userNits = BrightnessMappingStrategy.INVALID_NITS; - if (mAutomaticBrightnessController != null) { - userLux = mAutomaticBrightnessController.getUserLux(); - userNits = mAutomaticBrightnessController.getUserNits(); - } - - if (defaultModeBrightnessMapper != null) { - final float dozeScaleFactor = context.getResources().getFraction( - R.fraction.config_screenAutoBrightnessDozeScaleFactor, - 1, 1); - - // Ambient Lux - Active Mode Brightness Thresholds - float[] ambientBrighteningThresholds = - mDisplayDeviceConfig.getAmbientBrighteningPercentages(); - float[] ambientDarkeningThresholds = - mDisplayDeviceConfig.getAmbientDarkeningPercentages(); - float[] ambientBrighteningLevels = - mDisplayDeviceConfig.getAmbientBrighteningLevels(); - float[] ambientDarkeningLevels = - mDisplayDeviceConfig.getAmbientDarkeningLevels(); - float ambientDarkeningMinThreshold = - mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold(); - float ambientBrighteningMinThreshold = - mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(); - HysteresisLevels ambientBrightnessThresholds = mInjector.getHysteresisLevels( - ambientBrighteningThresholds, ambientDarkeningThresholds, - ambientBrighteningLevels, ambientDarkeningLevels, ambientDarkeningMinThreshold, - ambientBrighteningMinThreshold); - - // Display - Active Mode Brightness Thresholds - float[] screenBrighteningThresholds = - mDisplayDeviceConfig.getScreenBrighteningPercentages(); - float[] screenDarkeningThresholds = - mDisplayDeviceConfig.getScreenDarkeningPercentages(); - float[] screenBrighteningLevels = - mDisplayDeviceConfig.getScreenBrighteningLevels(); - float[] screenDarkeningLevels = - mDisplayDeviceConfig.getScreenDarkeningLevels(); - float screenDarkeningMinThreshold = - mDisplayDeviceConfig.getScreenDarkeningMinThreshold(); - float screenBrighteningMinThreshold = - mDisplayDeviceConfig.getScreenBrighteningMinThreshold(); - HysteresisLevels screenBrightnessThresholds = mInjector.getHysteresisLevels( - screenBrighteningThresholds, screenDarkeningThresholds, - screenBrighteningLevels, screenDarkeningLevels, screenDarkeningMinThreshold, - screenBrighteningMinThreshold, true); - - // Ambient Lux - Idle Screen Brightness Thresholds - float ambientDarkeningMinThresholdIdle = - mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle(); - float ambientBrighteningMinThresholdIdle = - mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle(); - float[] ambientBrighteningThresholdsIdle = - mDisplayDeviceConfig.getAmbientBrighteningPercentagesIdle(); - float[] ambientDarkeningThresholdsIdle = - mDisplayDeviceConfig.getAmbientDarkeningPercentagesIdle(); - float[] ambientBrighteningLevelsIdle = - mDisplayDeviceConfig.getAmbientBrighteningLevelsIdle(); - float[] ambientDarkeningLevelsIdle = - mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle(); - HysteresisLevels ambientBrightnessThresholdsIdle = mInjector.getHysteresisLevels( - ambientBrighteningThresholdsIdle, ambientDarkeningThresholdsIdle, - ambientBrighteningLevelsIdle, ambientDarkeningLevelsIdle, - ambientDarkeningMinThresholdIdle, ambientBrighteningMinThresholdIdle); - - // Display - Idle Screen Brightness Thresholds - float screenDarkeningMinThresholdIdle = - mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle(); - float screenBrighteningMinThresholdIdle = - mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle(); - float[] screenBrighteningThresholdsIdle = - mDisplayDeviceConfig.getScreenBrighteningPercentagesIdle(); - float[] screenDarkeningThresholdsIdle = - mDisplayDeviceConfig.getScreenDarkeningPercentagesIdle(); - float[] screenBrighteningLevelsIdle = - mDisplayDeviceConfig.getScreenBrighteningLevelsIdle(); - float[] screenDarkeningLevelsIdle = - mDisplayDeviceConfig.getScreenDarkeningLevelsIdle(); - HysteresisLevels screenBrightnessThresholdsIdle = mInjector.getHysteresisLevels( - screenBrighteningThresholdsIdle, screenDarkeningThresholdsIdle, - screenBrighteningLevelsIdle, screenDarkeningLevelsIdle, - screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle); - - long brighteningLightDebounce = mDisplayDeviceConfig - .getAutoBrightnessBrighteningLightDebounce(); - long darkeningLightDebounce = mDisplayDeviceConfig - .getAutoBrightnessDarkeningLightDebounce(); - long brighteningLightDebounceIdle = mDisplayDeviceConfig - .getAutoBrightnessBrighteningLightDebounceIdle(); - long darkeningLightDebounceIdle = mDisplayDeviceConfig - .getAutoBrightnessDarkeningLightDebounceIdle(); - boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean( - R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); - - int lightSensorWarmUpTimeConfig = context.getResources().getInteger( - R.integer.config_lightSensorWarmupTime); - int lightSensorRate = context.getResources().getInteger( - R.integer.config_autoBrightnessLightSensorRate); - int initialLightSensorRate = context.getResources().getInteger( - R.integer.config_autoBrightnessInitialLightSensorRate); - if (initialLightSensorRate == -1) { - initialLightSensorRate = lightSensorRate; - } else if (initialLightSensorRate > lightSensorRate) { - Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate (" - + initialLightSensorRate + ") to be less than or equal to " - + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ")."); - } - - loadAmbientLightSensor(); - // BrightnessTracker should only use one light sensor, we want to use the light sensor - // from the default display and not e.g. temporary displays when switching layouts. - if (mBrightnessTracker != null && mDisplayId == Display.DEFAULT_DISPLAY) { - mBrightnessTracker.setLightSensor(mLightSensor); - } - - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.stop(); - } - mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController( - this, handler.getLooper(), mSensorManager, mLightSensor, - brightnessMappers, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, - initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, - brighteningLightDebounceIdle, darkeningLightDebounceIdle, - autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, - screenBrightnessThresholds, ambientBrightnessThresholdsIdle, - screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, - mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(), - mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits); - mDisplayBrightnessController.setAutomaticBrightnessController( - mAutomaticBrightnessController); - - mAutomaticBrightnessStrategy - .setAutomaticBrightnessController(mAutomaticBrightnessController); - mBrightnessEventRingBuffer = - new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.stop(); - mScreenOffBrightnessSensorController = null; - } - loadScreenOffBrightnessSensor(); - int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux(); - if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) { - mScreenOffBrightnessSensorController = - mInjector.getScreenOffBrightnessSensorController( - mSensorManager, - mScreenOffBrightnessSensor, - mHandler, - SystemClock::uptimeMillis, - sensorValueToLux, - defaultModeBrightnessMapper); - } - } else { - mUseSoftwareAutoBrightnessConfig = false; - } - } - - private void loadBrightnessRampRates() { - mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease(); - mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease(); - mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease(); - mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease(); - mBrightnessRampRateSlowDecreaseIdle = - mDisplayDeviceConfig.getBrightnessRampSlowDecreaseIdle(); - mBrightnessRampRateSlowIncreaseIdle = - mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(); - mBrightnessRampDecreaseMaxTimeMillis = - mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(); - mBrightnessRampIncreaseMaxTimeMillis = - mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(); - mBrightnessRampDecreaseMaxTimeIdleMillis = - mDisplayDeviceConfig.getBrightnessRampDecreaseMaxIdleMillis(); - mBrightnessRampIncreaseMaxTimeIdleMillis = - mDisplayDeviceConfig.getBrightnessRampIncreaseMaxIdleMillis(); - } - - private void loadNitsRange(Resources resources) { - if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) { - mNitsRange = mDisplayDeviceConfig.getNits(); - } else { - Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back"); - mNitsRange = BrightnessMappingStrategy.getFloatArray(resources - .obtainTypedArray(R.array.config_screenBrightnessNits)); - } - } - - private void reloadReduceBrightColours() { - if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) { - applyReduceBrightColorsSplineAdjustment(); - } - } - - @Override - public void setAutomaticScreenBrightnessMode( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE; - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.switchMode(mode); - setAnimatorRampSpeeds(isIdle); - } - Message msg = mHandler.obtainMessage(); - msg.what = MSG_SET_DWBC_STRONG_MODE; - msg.arg1 = isIdle ? 1 : 0; - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - private void setAnimatorRampSpeeds(boolean isIdle) { - if (mScreenBrightnessRampAnimator == null) { - return; - } - if (mFlags.isAdaptiveTone1Enabled() && isIdle) { - mScreenBrightnessRampAnimator.setAnimationTimeLimits( - mBrightnessRampIncreaseMaxTimeIdleMillis, - mBrightnessRampDecreaseMaxTimeIdleMillis); - } else { - mScreenBrightnessRampAnimator.setAnimationTimeLimits( - mBrightnessRampIncreaseMaxTimeMillis, - mBrightnessRampDecreaseMaxTimeMillis); - } - } - - private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - sendUpdatePowerState(); - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - - @Override - public void onAnimationCancel(Animator animation) { - } - }; - - private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() { - @Override - public void onAnimationEnd() { - sendUpdatePowerState(); - Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - }; - - /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ - private void cleanupHandlerThreadAfterStop() { - mDisplayPowerProximityStateController.cleanup(); - mBrightnessRangeController.stop(); - mBrightnessThrottler.stop(); - mBrightnessClamperController.stop(); - mHandler.removeCallbacksAndMessages(null); - - // Release any outstanding wakelocks we're still holding because of pending messages. - mWakelockController.releaseAll(); - - final float brightness = mPowerState != null - ? mPowerState.getScreenBrightness() - : PowerManager.BRIGHTNESS_MIN; - reportStats(brightness); - - if (mPowerState != null) { - mPowerState.stop(); - mPowerState = null; - } - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.stop(); - } - - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setEnabled(false); - } - } - - // Call from handler thread - private void updatePowerState() { - Trace.traceBegin(Trace.TRACE_TAG_POWER, - "DisplayPowerController#updatePowerState"); - updatePowerStateInternal(); - Trace.traceEnd(Trace.TRACE_TAG_POWER); - } - - private void updatePowerStateInternal() { - // Update the power state request. - final boolean mustNotify; - final int previousPolicy; - boolean mustInitialize = false; - mBrightnessReasonTemp.set(null); - mTempBrightnessEvent.reset(); - SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers; - synchronized (mLock) { - if (mStopped) { - return; - } - mPendingUpdatePowerStateLocked = false; - if (mPendingRequestLocked == null) { - return; // wait until first actual power request - } - - if (mPowerRequest == null) { - mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); - mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); - mPendingRequestChangedLocked = false; - mustInitialize = true; - // Assume we're on and bright until told otherwise, since that's the state we turn - // on in. - previousPolicy = DisplayPowerRequest.POLICY_BRIGHT; - } else if (mPendingRequestChangedLocked) { - previousPolicy = mPowerRequest.policy; - mPowerRequest.copyFrom(mPendingRequestLocked); - mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked(); - mPendingRequestChangedLocked = false; - mDisplayReadyLocked = false; - } else { - previousPolicy = mPowerRequest.policy; - } - - mustNotify = !mDisplayReadyLocked; - - displayBrightnessFollowers = mDisplayBrightnessFollowers.clone(); - } - - int state = mDisplayStateController - .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition); - - // Initialize things the first time the power state is changed. - if (mustInitialize) { - initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN); - } - - // Animate the screen state change unless already animating. - // The transition may be deferred, so after this point we will use the - // actual state instead of the desired one. - animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition()); - state = mPowerState.getScreenState(); - - // Switch to doze auto-brightness mode if needed - if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null - && !mAutomaticBrightnessController.isInIdleMode()) { - setAutomaticScreenBrightnessMode(Display.isDozeState(state) - ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); - } - - final boolean userSetBrightnessChanged = mDisplayBrightnessController - .updateUserSetScreenBrightness(); - - DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController - .updateBrightness(mPowerRequest, state); - float brightnessState = displayBrightnessState.getBrightness(); - float rawBrightnessState = displayBrightnessState.getBrightness(); - mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason()); - boolean slowChange = displayBrightnessState.isSlowChange(); - // custom transition duration - float customAnimationRate = displayBrightnessState.getCustomAnimationRate(); - - // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor - // doesn't yet have a valid lux value to use with auto-brightness. - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController - .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness() - && mIsEnabled && (state == Display.STATE_OFF - || (state == Display.STATE_DOZE - && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())) - && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); - } - - // Take note if the short term model was already active before applying the current - // request changes. - final boolean wasShortTermModelActive = - mAutomaticBrightnessStrategy.isShortTermModelActive(); - mAutomaticBrightnessStrategy.setAutoBrightnessState(state, - mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(), - mBrightnessReasonTemp.getReason(), mPowerRequest.policy, - mDisplayBrightnessController.getLastUserSetScreenBrightness(), - userSetBrightnessChanged); - - // If the brightness is already set then it's been overridden by something other than the - // user, or is a temporary adjustment. - boolean userInitiatedChange = (Float.isNaN(brightnessState)) - && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged() - || userSetBrightnessChanged); - - mBrightnessRangeController.setAutoBrightnessEnabled( - mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff() - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); - - boolean updateScreenBrightnessSetting = - displayBrightnessState.shouldUpdateScreenBrightnessSetting(); - float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness(); - // Apply auto-brightness. - int brightnessAdjustmentFlags = 0; - if (Float.isNaN(brightnessState)) { - if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) { - brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness( - mTempBrightnessEvent); - if (BrightnessUtils.isValidBrightnessValue(brightnessState) - || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { - rawBrightnessState = mAutomaticBrightnessController - .getRawAutomaticScreenBrightness(); - brightnessState = clampScreenBrightness(brightnessState); - // slowly adapt to auto-brightness - // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness - slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness() - && !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged(); - brightnessAdjustmentFlags = - mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentReasonsFlags(); - updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState; - mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true); - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.setLightSensorEnabled(false); - } - } else { - mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); - } - } - } else { - // Any non-auto-brightness values such as override or temporary should still be subject - // to clamping so that they don't go beyond the current max as specified by HBM - // Controller. - brightnessState = clampScreenBrightness(brightnessState); - mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); - } - - // Use default brightness when dozing unless overridden. - if ((Float.isNaN(brightnessState)) - && Display.isDozeState(state)) { - rawBrightnessState = mScreenBrightnessDozeConfig; - brightnessState = clampScreenBrightness(rawBrightnessState); - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); - } - - // The ALS is not available yet - use the screen off sensor to determine the initial - // brightness - if (Float.isNaN(brightnessState) && mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() - && mScreenOffBrightnessSensorController != null) { - rawBrightnessState = - mScreenOffBrightnessSensorController.getAutomaticScreenBrightness(); - brightnessState = rawBrightnessState; - if (BrightnessUtils.isValidBrightnessValue(brightnessState)) { - brightnessState = clampScreenBrightness(brightnessState); - updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness() - != brightnessState; - mBrightnessReasonTemp.setReason( - BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR); - } - } - - // Apply manual brightness. - if (Float.isNaN(brightnessState)) { - rawBrightnessState = currentBrightnessSetting; - brightnessState = clampScreenBrightness(rawBrightnessState); - if (brightnessState != currentBrightnessSetting) { - // The manually chosen screen brightness is outside of the currently allowed - // range (i.e., high-brightness-mode), make sure we tell the rest of the system - // by updating the setting. - updateScreenBrightnessSetting = true; - } - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); - } - - float ambientLux = mAutomaticBrightnessController == null ? 0 - : mAutomaticBrightnessController.getAmbientLux(); - for (int i = 0; i < displayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); - follower.setBrightnessToFollow(rawBrightnessState, - mDisplayBrightnessController.convertToNits(rawBrightnessState), - ambientLux, slowChange); - } - - // Now that a desired brightness has been calculated, apply brightness throttling. The - // dimming and low power transformations that follow can only dim brightness further. - // - // We didn't do this earlier through brightness clamping because we need to know both - // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations. - // Note throttling effectively changes the allowed brightness range, so, similarly to HBM, - // we broadcast this change through setting. - final float unthrottledBrightnessState = brightnessState; - DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, - brightnessState, slowChange); - - brightnessState = clampedState.getBrightness(); - slowChange = clampedState.isSlowChange(); - // faster rate wins, at this point customAnimationRate == -1, strategy does not control - // customAnimationRate. Should be revisited if strategy start setting this value - customAnimationRate = Math.max(customAnimationRate, clampedState.getCustomAnimationRate()); - mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier()); - - if (updateScreenBrightnessSetting) { - // Tell the rest of the system about the new brightness in case we had to change it - // for things like auto-brightness or high-brightness-mode. Note that we do this - // only considering maxBrightness (ignoring brightness modifiers like low power or dim) - // so that the slider accurately represents the full possible range, - // even if they range changes what it means in absolute terms. - mDisplayBrightnessController.updateScreenBrightnessSetting( - MathUtils.constrain(unthrottledBrightnessState, - clampedState.getMinBrightness(), clampedState.getMaxBrightness())); - } - - // The current brightness to use has been calculated at this point, and HbmController should - // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it - // here instead of having HbmController listen to the brightness setting because certain - // brightness sources (such as an app override) are not saved to the setting, but should be - // reflected in HBM calculations. - mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, - mBrightnessClamperController.getBrightnessMaxReason()); - - // Animate the screen brightness when the screen is on or dozing. - // Skip the animation when the screen is off or suspended. - boolean brightnessAdjusted = false; - final boolean brightnessIsTemporary = - (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY) - || mAutomaticBrightnessStrategy - .isTemporaryAutoBrightnessAdjustmentApplied(); - if (!mPendingScreenOff) { - if (mSkipScreenOnBrightnessRamp) { - if (state == Display.STATE_ON) { - if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) { - mInitialAutoBrightness = brightnessState; - mSkipRampState = RAMP_STATE_SKIP_INITIAL; - } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL - && mUseSoftwareAutoBrightnessConfig - && !BrightnessSynchronizer.floatEquals(brightnessState, - mInitialAutoBrightness)) { - mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT; - } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) { - mSkipRampState = RAMP_STATE_SKIP_NONE; - } - } else { - mSkipRampState = RAMP_STATE_SKIP_NONE; - } - } - - final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState - != RAMP_STATE_SKIP_NONE) || mDisplayPowerProximityStateController - .shouldSkipRampBecauseOfProximityChangeToNegative(); - // While dozing, sometimes the brightness is split into buckets. Rather than animating - // through the buckets, which is unlikely to be smooth in the first place, just jump - // right to the suggested brightness. - final boolean hasBrightnessBuckets = - Display.isDozeState(state) && mBrightnessBucketsInDozeConfig; - // If the color fade is totally covering the screen then we can change the backlight - // level without it being a noticeable jump since any actual content isn't yet visible. - final boolean isDisplayContentVisible = - mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f; - // We only want to animate the brightness if it is between 0.0f and 1.0f. - // brightnessState can contain the values -1.0f and NaN, which we do not want to - // animate to. To avoid this, we check the value first. - // If the brightnessState is off (-1.0f) we still want to animate to the minimum - // brightness (0.0f) to accommodate for LED displays, which can appear bright to the - // user even when the display is all black. We also clamp here in case some - // transformations to the brightness have pushed it outside of the currently - // allowed range. - float animateValue = clampScreenBrightness(brightnessState); - - // If there are any HDR layers on the screen, we have a special brightness value that we - // use instead. We still preserve the calculated brightness for Standard Dynamic Range - // (SDR) layers, but the main brightness value will be the one for HDR. - float sdrAnimateValue = animateValue; - // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be - // done in HighBrightnessModeController. - if (mBrightnessRangeController.getHighBrightnessMode() - == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR - && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0 - && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER) - == 0) { - // We want to scale HDR brightness level with the SDR level, we also need to restore - // SDR brightness immediately when entering dim or low power mode. - animateValue = mBrightnessRangeController.getHdrBrightnessValue(); - customAnimationRate = Math.max(customAnimationRate, - mBrightnessRangeController.getHdrTransitionRate()); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR); - } - - // if doze or suspend state is requested, we want to finish brightnes animation fast - // to allow state animation to start - if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE - && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN // dozing - || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND - || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) { - customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; - slowChange = false; - } - - final float currentBrightness = mPowerState.getScreenBrightness(); - final float currentSdrBrightness = mPowerState.getSdrScreenBrightness(); - - if (BrightnessUtils.isValidBrightnessValue(animateValue) - && (animateValue != currentBrightness - || sdrAnimateValue != currentSdrBrightness)) { - boolean skipAnimation = initialRampSkip || hasBrightnessBuckets - || !isDisplayContentVisible || brightnessIsTemporary; - final boolean isHdrOnlyChange = BrightnessSynchronizer.floatEquals( - sdrAnimateValue, currentSdrBrightness); - if (mFlags.isFastHdrTransitionsEnabled() && !skipAnimation && isHdrOnlyChange) { - // SDR brightness is unchanged, so animate quickly as this is only impacting - // a likely minority amount of display content - // ie, the highlights of an HDR video or UltraHDR image - slowChange = false; - - // Going from HDR to no HDR; visually this should be a "no-op" anyway - // as the remaining SDR content's brightness should be holding steady - // due to the sdr brightness not shifting - if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) { - skipAnimation = true; - } - - // Going from no HDR to HDR; visually this is a significant scene change - // and the animation just prevents advanced clients from doing their own - // handling of enter/exit animations if they would like to do such a thing - if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) { - skipAnimation = true; - } - } - if (skipAnimation) { - animateScreenBrightness(animateValue, sdrAnimateValue, - SCREEN_ANIMATION_RATE_MINIMUM); - } else if (customAnimationRate > 0) { - animateScreenBrightness(animateValue, sdrAnimateValue, - customAnimationRate, /* ignoreAnimationLimits = */true); - } else { - boolean isIncreasing = animateValue > currentBrightness; - final float rampSpeed; - final boolean idle = mAutomaticBrightnessController != null - && mAutomaticBrightnessController.isInIdleMode(); - if (isIncreasing && slowChange) { - rampSpeed = idle ? mBrightnessRampRateSlowIncreaseIdle - : mBrightnessRampRateSlowIncrease; - } else if (isIncreasing && !slowChange) { - rampSpeed = mBrightnessRampRateFastIncrease; - } else if (!isIncreasing && slowChange) { - rampSpeed = idle ? mBrightnessRampRateSlowDecreaseIdle - : mBrightnessRampRateSlowDecrease; - } else { - rampSpeed = mBrightnessRampRateFastDecrease; - } - animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed); - } - } - - notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(), - brightnessIsTemporary, displayBrightnessState.getShouldUseAutoBrightness()); - - // We save the brightness info *after* the brightness setting has been changed and - // adjustments made so that the brightness info reflects the latest value. - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), - animateValue, clampedState); - } else { - brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), clampedState); - } - - // Only notify if the brightness adjustment is not temporary (i.e. slider has been released) - if (brightnessAdjusted && !brightnessIsTemporary) { - postBrightnessChangeRunnable(); - } - - // Log any changes to what is currently driving the brightness setting. - if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) { - Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '" - + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags) - + "', previous reason: '" + mBrightnessReason + "'."); - mBrightnessReason.set(mBrightnessReasonTemp); - } else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL - && userSetBrightnessChanged) { - Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment."); - } - - - // Log brightness events when a detail of significance has changed. Generally this is the - // brightness itself changing, but also includes data like HBM cap, thermal throttling - // brightness cap, RBC state, etc. - mTempBrightnessEvent.setTime(System.currentTimeMillis()); - mTempBrightnessEvent.setBrightness(brightnessState); - mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId); - mTempBrightnessEvent.setReason(mBrightnessReason); - mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax()); - mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode()); - mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags() - | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0) - | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0)); - mTempBrightnessEvent.setRbcStrength(mCdsi != null - ? mCdsi.getReduceBrightColorsStrength() : -1); - mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); - mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); - mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState - .getDisplayBrightnessStrategyName()); - mTempBrightnessEvent.setAutomaticBrightnessEnabled( - displayBrightnessState.getShouldUseAutoBrightness()); - // Temporary is what we use during slider interactions. We avoid logging those so that - // we don't spam logcat when the slider is being used. - boolean tempToTempTransition = - mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY - && mLastBrightnessEvent.getReason().getReason() - == BrightnessReason.REASON_TEMPORARY; - // Purely for dumpsys; - final boolean isRbcEvent = - mLastBrightnessEvent.isRbcEnabled() != mTempBrightnessEvent.isRbcEnabled(); - - if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition) - || brightnessAdjustmentFlags != 0) { - mTempBrightnessEvent.setInitialBrightness(mLastBrightnessEvent.getBrightness()); - mLastBrightnessEvent.copyFrom(mTempBrightnessEvent); - BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent); - // Adjustment flags (and user-set flag) only get added after the equality checks since - // they are transient. - newEvent.setAdjustmentFlags(brightnessAdjustmentFlags); - newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged - ? BrightnessEvent.FLAG_USER_SET : 0)); - Slog.i(mTag, newEvent.toString(/* includeTime= */ false)); - - if (userSetBrightnessChanged - || newEvent.getReason().getReason() != BrightnessReason.REASON_TEMPORARY) { - logBrightnessEvent(newEvent, unthrottledBrightnessState); - } - if (mBrightnessEventRingBuffer != null) { - mBrightnessEventRingBuffer.append(newEvent); - } - if (isRbcEvent) { - mRbcEventRingBuffer.append(newEvent); - } - - } - - // Update display white-balance. - if (mDisplayWhiteBalanceController != null) { - if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) { - mDisplayWhiteBalanceController.setEnabled(true); - mDisplayWhiteBalanceController.updateDisplayColorTemperature(); - } else { - mDisplayWhiteBalanceController.setEnabled(false); - } - } - - // Determine whether the display is ready for use in the newly requested state. - // Note that we do not wait for the brightness ramp animation to complete before - // reporting the display is ready because we only need to ensure the screen is in the - // right power state even as it continues to converge on the desired brightness. - final boolean ready = mPendingScreenOnUnblocker == null - && mPendingScreenOnUnblockerByDisplayOffload == null - && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted() - && !mColorFadeOffAnimator.isStarted())) - && mPowerState.waitUntilClean(mCleanListener); - final boolean finished = ready - && !mScreenBrightnessRampAnimator.isAnimating(); - - // Notify policy about screen turned on. - if (ready && state != Display.STATE_OFF - && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON); - mWindowManagerPolicy.screenTurnedOn(mDisplayId); - } - - // Grab a wake lock if we have unfinished business. - if (!finished) { - mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - } - - // Notify the power manager when ready. - if (ready && mustNotify) { - // Send state change. - synchronized (mLock) { - if (!mPendingRequestChangedLocked) { - mDisplayReadyLocked = true; - - if (DEBUG) { - Slog.d(mTag, "Display ready!"); - } - } - } - sendOnStateChangedWithWakelock(); - } - - // Release the wake lock when we have no unfinished business. - if (finished) { - mWakelockController.releaseWakelock(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - } - - // Record if dozing for future comparison. - mDozing = state != Display.STATE_ON; - - if (previousPolicy != mPowerRequest.policy) { - logDisplayPolicyChanged(mPowerRequest.policy); - } - } - - private void setDwbcOverride(float cct) { - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct); - // The ambient color temperature override is only applied when the ambient color - // temperature changes or is updated, so it doesn't necessarily change the screen color - // temperature immediately. So, let's make it! - // We can call this directly, since we're already on the handler thread. - updatePowerState(); - } - } - - private void setDwbcStrongMode(int arg) { - if (mDisplayWhiteBalanceController != null) { - final boolean isIdle = (arg == 1); - mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); - } - } - - private void setDwbcLoggingEnabled(int arg) { - if (mDisplayWhiteBalanceController != null) { - final boolean enabled = (arg == 1); - mDisplayWhiteBalanceController.setLoggingEnabled(enabled); - mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled); - } - } - - @Override - public void updateBrightness() { - sendUpdatePowerState(); - } - - /** - * Ignores the proximity sensor until the sensor state changes, but only if the sensor is - * currently enabled and forcing the screen to be dark. - */ - @Override - public void ignoreProximitySensorUntilChanged() { - mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged(); - } - - @Override - public void setBrightnessConfiguration(BrightnessConfiguration c, - boolean shouldResetShortTermModel) { - Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, - shouldResetShortTermModel ? 1 : 0, /* unused */ 0, c); - msg.sendToTarget(); - } - - @Override - public void setTemporaryBrightness(float brightness) { - Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS, - Float.floatToIntBits(brightness), 0 /*unused*/); - msg.sendToTarget(); - } - - @Override - public void setTemporaryAutoBrightnessAdjustment(float adjustment) { - Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT, - Float.floatToIntBits(adjustment), 0 /*unused*/); - msg.sendToTarget(); - } - - @Override - public void setBrightnessFromOffload(float brightness) { - Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD, - Float.floatToIntBits(brightness), 0 /*unused*/); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - @Override - public float[] getAutoBrightnessLevels( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - int preset = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, - Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); - return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); - } - - @Override - public float[] getAutoBrightnessLuxLevels( - @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - int preset = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, - Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); - return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); - } - - @Override - public BrightnessInfo getBrightnessInfo() { - synchronized (mCachedBrightnessInfo) { - return new BrightnessInfo( - mCachedBrightnessInfo.brightness.value, - mCachedBrightnessInfo.adjustedBrightness.value, - mCachedBrightnessInfo.brightnessMin.value, - mCachedBrightnessInfo.brightnessMax.value, - mCachedBrightnessInfo.hbmMode.value, - mCachedBrightnessInfo.hbmTransitionPoint.value, - mCachedBrightnessInfo.brightnessMaxReason.value); - } - } - - @Override - public void onBootCompleted() { - Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - - private boolean saveBrightnessInfo(float brightness) { - return saveBrightnessInfo(brightness, /* state= */ null); - } - - private boolean saveBrightnessInfo(float brightness, @Nullable DisplayBrightnessState state) { - return saveBrightnessInfo(brightness, brightness, state); - } - - private boolean saveBrightnessInfo(float brightness, float adjustedBrightness, - @Nullable DisplayBrightnessState state) { - synchronized (mCachedBrightnessInfo) { - float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX; - float stateMin = state != null ? state.getMinBrightness() : PowerManager.BRIGHTNESS_MAX; - final float minBrightness = Math.max(stateMin, Math.min( - mBrightnessRangeController.getCurrentBrightnessMin(), stateMax)); - final float maxBrightness = Math.min( - mBrightnessRangeController.getCurrentBrightnessMax(), stateMax); - boolean changed = false; - - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness, - brightness); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness, - adjustedBrightness); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin, - minBrightness); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax, - maxBrightness); - changed |= - mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode, - mBrightnessRangeController.getHighBrightnessMode()); - changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint, - mBrightnessRangeController.getTransitionPoint()); - changed |= - mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, - mBrightnessClamperController.getBrightnessMaxReason()); - return changed; - } - } - - void postBrightnessChangeRunnable() { - if (!mHandler.hasCallbacks(mOnBrightnessChangeRunnable)) { - mHandler.post(mOnBrightnessChangeRunnable); - } - } - - private HighBrightnessModeController createHbmControllerLocked( - HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) { - final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig(); - final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked(); - final String displayUniqueId = mDisplayDevice.getUniqueId(); - final DisplayDeviceConfig.HighBrightnessModeData hbmData = - ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; - final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked(); - return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height, - displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) -> - mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness, - maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext); - } - - private BrightnessThrottler createBrightnessThrottlerLocked() { - final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); - final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); - return new BrightnessThrottler(mHandler, - () -> { - sendUpdatePowerState(); - postBrightnessChangeRunnable(); - }, mUniqueDisplayId, - mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId, - ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId()); - } - - private void blockScreenOn() { - if (mPendingScreenOnUnblocker == null) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); - mPendingScreenOnUnblocker = new ScreenOnUnblocker(); - mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime(); - Slog.i(mTag, "Blocking screen on until initial contents have been drawn."); - } - } - - private void unblockScreenOn() { - if (mPendingScreenOnUnblocker != null) { - mPendingScreenOnUnblocker = null; - long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime; - Slog.i(mTag, "Unblocked screen on after " + delay + " ms"); - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); - } - } - - private void blockScreenOff() { - if (mPendingScreenOffUnblocker == null) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0); - mPendingScreenOffUnblocker = new ScreenOffUnblocker(); - mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime(); - Slog.i(mTag, "Blocking screen off"); - } - } - - private void unblockScreenOff() { - if (mPendingScreenOffUnblocker != null) { - mPendingScreenOffUnblocker = null; - long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime; - Slog.i(mTag, "Unblocked screen off after " + delay + " ms"); - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0); - } - } - - private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) { - if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) { - return; - } - mScreenTurningOnWasBlockedByDisplayOffload = true; - - Trace.asyncTraceBegin( - Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); - mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime(); - - mPendingScreenOnUnblockerByDisplayOffload = - () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession); - if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) { - mPendingScreenOnUnblockerByDisplayOffload = null; - long delay = - SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; - Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after " - + delay + " ms."); - Trace.asyncTraceEnd( - Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); - return; - } - Slog.i(mTag, "Blocking screen on for offloading."); - } - - private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) { - Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED, - displayOffloadSession); - mHandler.sendMessage(msg); - } - - private void unblockScreenOnByDisplayOffload() { - if (mPendingScreenOnUnblockerByDisplayOffload == null) { - return; - } - mPendingScreenOnUnblockerByDisplayOffload = null; - long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime; - Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms"); - Trace.asyncTraceEnd( - Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0); - } - - private boolean setScreenState(int state) { - return setScreenState(state, false /*reportOnly*/); - } - - private boolean setScreenState(int state, boolean reportOnly) { - final boolean isOff = (state == Display.STATE_OFF); - final boolean isOn = (state == Display.STATE_ON); - final boolean changed = mPowerState.getScreenState() != state; - - // If the screen is turning on, give displayoffload a chance to do something before the - // screen actually turns on. - // TODO(b/316941732): add tests for this displayoffload screen-on blocker. - if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) { - blockScreenOnByDisplayOffload(mDisplayOffloadSession); - } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) { - // No longer turning screen on, so unblock previous screen on blocking immediately. - unblockScreenOnByDisplayOffload(); - mScreenTurningOnWasBlockedByDisplayOffload = false; - } - - if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { - // If we are trying to turn screen off, give policy a chance to do something before we - // actually turn the screen off. - if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { - if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON - || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF); - blockScreenOff(); - mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker); - unblockScreenOff(); - } else if (mPendingScreenOffUnblocker != null) { - // Abort doing the state change until screen off is unblocked. - return false; - } - } - - if (!reportOnly && changed && readyToUpdateDisplayState() - && mPendingScreenOffUnblocker == null - && mPendingScreenOnUnblockerByDisplayOffload == null) { - Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state); - - String propertyKey = "debug.tracing.screen_state"; - String propertyValue = String.valueOf(state); - try { - // TODO(b/153319140) remove when we can get this from the above trace invocation - SystemProperties.set(propertyKey, propertyValue); - } catch (RuntimeException e) { - Slog.e(mTag, "Failed to set a system property: key=" + propertyKey - + " value=" + propertyValue + " " + e.getMessage()); - } - - mPowerState.setScreenState(state); - // Tell battery stats about the transition. - noteScreenState(state); - } - } - - // Tell the window manager policy when the screen is turned off or on unless it's due - // to the proximity sensor. We temporarily block turning the screen on until the - // window manager is ready by leaving a black surface covering the screen. - // This surface is essentially the final state of the color fade animation and - // it is only removed once the window manager tells us that the activity has - // finished drawing underneath. - if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF - && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); - unblockScreenOn(); - mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); - } else if (!isOff - && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) { - - // We told policy already that screen was turning off, but now we changed our minds. - // Complete the full state transition on -> turningOff -> off. - unblockScreenOff(); - mWindowManagerPolicy.screenTurnedOff(mDisplayId, mIsInTransition); - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); - } - if (!isOff - && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF - || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) { - setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON); - if (mPowerState.getColorFadeLevel() == 0.0f) { - blockScreenOn(); - } else { - unblockScreenOn(); - } - mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker); - } - - // Return true if the screen isn't blocked. - return mPendingScreenOnUnblocker == null - && mPendingScreenOnUnblockerByDisplayOffload == null; - } - - private void setReportedScreenState(int state) { - Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state); - mReportedScreenStateToPolicy = state; - if (state == REPORTED_TO_POLICY_SCREEN_ON) { - mScreenTurningOnWasBlockedByDisplayOffload = false; - } - } - - private void loadAmbientLightSensor() { - final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY - ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK; - mLightSensor = SensorUtils.findSensor(mSensorManager, - mDisplayDeviceConfig.getAmbientLightSensor(), fallbackType); - } - - private void loadScreenOffBrightnessSensor() { - mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager, - mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK); - } - - private float clampScreenBrightness(float value) { - if (Float.isNaN(value)) { - value = PowerManager.BRIGHTNESS_MIN; - } - return MathUtils.constrain(value, mBrightnessRangeController.getCurrentBrightnessMin(), - mBrightnessRangeController.getCurrentBrightnessMax()); - } - - private void animateScreenBrightness(float target, float sdrTarget, float rate) { - animateScreenBrightness(target, sdrTarget, rate, /* ignoreAnimationLimits = */false); - } - - private void animateScreenBrightness(float target, float sdrTarget, float rate, - boolean ignoreAnimationLimits) { - if (DEBUG) { - Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget - + ", rate=" + rate); - } - if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate, - ignoreAnimationLimits)) { - Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target); - - String propertyKey = "debug.tracing.screen_brightness"; - String propertyValue = String.valueOf(target); - try { - // TODO(b/153319140) remove when we can get this from the above trace invocation - SystemProperties.set(propertyKey, propertyValue); - } catch (RuntimeException e) { - Slog.e(mTag, "Failed to set a system property: key=" + propertyKey - + " value=" + propertyValue + " " + e.getMessage()); - } - - noteScreenBrightness(target); - } - } - - private void animateScreenStateChange(int target, boolean performScreenOffTransition) { - // If there is already an animation in progress, don't interfere with it. - if (mColorFadeEnabled - && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) { - if (target != Display.STATE_ON) { - return; - } - // If display state changed to on, proceed and stop the color fade and turn screen on. - mPendingScreenOff = false; - } - - if (mDisplayBlanksAfterDozeConfig - && Display.isDozeState(mPowerState.getScreenState()) - && !Display.isDozeState(target)) { - // Skip the screen off animation and add a black surface to hide the - // contents of the screen. - mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP); - if (mColorFadeOffAnimator != null) { - mColorFadeOffAnimator.end(); - } - // Some display hardware will blank itself on the transition between doze and non-doze - // but still on display states. In this case we want to report to policy that the - // display has turned off so it can prepare the appropriate power on animation, but we - // don't want to actually transition to the fully off state since that takes - // significantly longer to transition from. - setScreenState(Display.STATE_OFF, target != Display.STATE_OFF /*reportOnly*/); - } - - // If we were in the process of turning off the screen but didn't quite - // finish. Then finish up now to prevent a jarring transition back - // to screen on if we skipped blocking screen on as usual. - if (mPendingScreenOff && target != Display.STATE_OFF) { - setScreenState(Display.STATE_OFF); - mPendingScreenOff = false; - mPowerState.dismissColorFadeResources(); - } - - if (target == Display.STATE_ON) { - // Want screen on. The contents of the screen may not yet - // be visible if the color fade has not been dismissed because - // its last frame of animation is solid black. - if (!setScreenState(Display.STATE_ON)) { - return; // screen on blocked - } - if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) { - // Perform screen on animation. - if (mPowerState.getColorFadeLevel() == 1.0f) { - mPowerState.dismissColorFade(); - } else if (mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig - ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) { - mColorFadeOnAnimator.start(); - } else { - mColorFadeOnAnimator.end(); - } - } else { - // Skip screen on animation. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } - } else if (target == Display.STATE_DOZE) { - // Want screen dozing. - // Wait for brightness animation to complete beforehand when entering doze - // from screen on to prevent a perceptible jump because brightness may operate - // differently when the display is configured for dozing. - if (mScreenBrightnessRampAnimator.isAnimating() - && mPowerState.getScreenState() == Display.STATE_ON) { - return; - } - - // Set screen state. - if (!setScreenState(Display.STATE_DOZE)) { - return; // screen on blocked - } - - // Dismiss the black surface without fanfare. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } else if (target == Display.STATE_DOZE_SUSPEND) { - // Want screen dozing and suspended. - // Wait for brightness animation to complete beforehand unless already - // suspended because we may not be able to change it after suspension. - if (mScreenBrightnessRampAnimator.isAnimating() - && mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) { - return; - } - - // If not already suspending, temporarily set the state to doze until the - // screen on is unblocked, then suspend. - if (mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) { - if (!setScreenState(Display.STATE_DOZE)) { - return; // screen on blocked - } - setScreenState(Display.STATE_DOZE_SUSPEND); // already on so can't block - } - - // Dismiss the black surface without fanfare. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } else if (target == Display.STATE_ON_SUSPEND) { - // Want screen full-power and suspended. - // Wait for brightness animation to complete beforehand unless already - // suspended because we may not be able to change it after suspension. - if (mScreenBrightnessRampAnimator.isAnimating() - && mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) { - return; - } - - // If not already suspending, temporarily set the state to on until the - // screen on is unblocked, then suspend. - if (mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) { - if (!setScreenState(Display.STATE_ON)) { - return; - } - setScreenState(Display.STATE_ON_SUSPEND); - } - - // Dismiss the black surface without fanfare. - mPowerState.setColorFadeLevel(1.0f); - mPowerState.dismissColorFade(); - } else { - // Want screen off. - mPendingScreenOff = true; - if (!mColorFadeEnabled) { - mPowerState.setColorFadeLevel(0.0f); - } - - if (mPowerState.getColorFadeLevel() == 0.0f) { - // Turn the screen off. - // A black surface is already hiding the contents of the screen. - setScreenState(Display.STATE_OFF); - mPendingScreenOff = false; - mPowerState.dismissColorFadeResources(); - } else if (performScreenOffTransition - && mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig - ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN) - && mPowerState.getScreenState() != Display.STATE_OFF) { - // Perform the screen off animation. - mColorFadeOffAnimator.start(); - } else { - // Skip the screen off animation and add a black surface to hide the - // contents of the screen. - mColorFadeOffAnimator.end(); - } - } - } - - private final Runnable mCleanListener = this::sendUpdatePowerState; - - private void sendOnStateChangedWithWakelock() { - boolean wakeLockAcquired = mWakelockController.acquireWakelock( - WakelockController.WAKE_LOCK_STATE_CHANGED); - if (wakeLockAcquired) { - mHandler.post(mWakelockController.getOnStateChangedRunnable()); - } - } - - private void logDisplayPolicyChanged(int newPolicy) { - LogMaker log = new LogMaker(MetricsEvent.DISPLAY_POLICY); - log.setType(MetricsEvent.TYPE_UPDATE); - log.setSubtype(newPolicy); - MetricsLogger.action(log); - } - - private void handleSettingsChange(boolean userSwitch) { - mDisplayBrightnessController - .setPendingScreenBrightness(mDisplayBrightnessController - .getScreenBrightnessSetting()); - mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(userSwitch); - if (userSwitch) { - // Don't treat user switches as user initiated change. - mDisplayBrightnessController - .setAndNotifyCurrentScreenBrightness(mDisplayBrightnessController - .getPendingScreenBrightness()); - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.resetShortTermModel(); - } - } - sendUpdatePowerState(); - } - - private void handleBrightnessModeChange() { - final int screenBrightnessModeSetting = Settings.System.getIntForUser( - mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); - mHandler.postAtTime(() -> { - mAutomaticBrightnessStrategy.setUseAutoBrightness(screenBrightnessModeSetting - == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - updatePowerState(); - }, mClock.uptimeMillis()); - } - - - @Override - public float getScreenBrightnessSetting() { - return mDisplayBrightnessController.getScreenBrightnessSetting(); - } - - @Override - public void setBrightness(float brightnessValue, int userSerial) { - mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue), - userSerial); - } - - @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override - public int getLeadDisplayId() { - return mLeadDisplayId; - } - - @Override - public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, - boolean slowChange) { - mBrightnessRangeController.onAmbientLuxChange(ambientLux); - if (nits == BrightnessMappingStrategy.INVALID_NITS) { - mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange); - } else { - float brightness = mDisplayBrightnessController.getBrightnessFromNits(nits); - if (BrightnessUtils.isValidBrightnessValue(brightness)) { - mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange); - } else { - // The device does not support nits - mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, - slowChange); - } - } - sendUpdatePowerState(); - } - - private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, - boolean wasShortTermModelActive, boolean autobrightnessEnabled, - boolean brightnessIsTemporary, boolean shouldUseAutoBrightness) { - - final float brightnessInNits = - mDisplayBrightnessController.convertToAdjustedNits(brightness); - // Don't report brightness to brightnessTracker: - // If brightness is temporary (ie the slider has not been released) - // or if we are in idle screen brightness mode. - // or display is not on - // or we shouldn't be using autobrightness - // or the nits is invalid. - if (brightnessIsTemporary - || mAutomaticBrightnessController == null - || mAutomaticBrightnessController.isInIdleMode() - || !autobrightnessEnabled - || mBrightnessTracker == null - || !shouldUseAutoBrightness - || brightnessInNits < 0.0f) { - return; - } - - if (userInitiated && (mAutomaticBrightnessController == null - || !mAutomaticBrightnessController.hasValidAmbientLux())) { - // If we don't have a valid lux reading we can't report a valid - // slider event so notify as if the system changed the brightness. - userInitiated = false; - } - - // We only want to track changes on devices that can actually map the display backlight - // values into a physical brightness unit since the value provided by the API is in - // nits and not using the arbitrary backlight units. - final float powerFactor = mPowerRequest.lowPowerMode - ? mPowerRequest.screenLowPowerBrightnessFactor - : 1.0f; - mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, - powerFactor, wasShortTermModelActive, - mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId, - mAutomaticBrightnessController.getLastSensorValues(), - mAutomaticBrightnessController.getLastSensorTimestamps()); - } - - @Override - public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower); - sendUpdatePowerStateLocked(); - } - } - - @Override - public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) { - synchronized (mLock) { - mDisplayBrightnessFollowers.remove(follower.getDisplayId()); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - } - - @GuardedBy("mLock") - private void clearDisplayBrightnessFollowersLocked() { - for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); - mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, - /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); - } - mDisplayBrightnessFollowers.clear(); - } - - @Override - public void dump(final PrintWriter pw) { - synchronized (mLock) { - pw.println(); - pw.println("Display Power Controller:"); - pw.println(" mDisplayId=" + mDisplayId); - pw.println(" mLeadDisplayId=" + mLeadDisplayId); - pw.println(" mLightSensor=" + mLightSensor); - pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers); - - pw.println(); - pw.println("Display Power Controller Locked State:"); - pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked); - pw.println(" mPendingRequestLocked=" + mPendingRequestLocked); - pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked); - pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked); - } - - pw.println(); - pw.println("Display Power Controller Configuration:"); - pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); - pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); - pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp); - pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig); - pw.println(" mColorFadeEnabled=" + mColorFadeEnabled); - pw.println(" mIsDisplayInternal=" + mIsDisplayInternal); - synchronized (mCachedBrightnessInfo) { - pw.println(" mCachedBrightnessInfo.brightness=" - + mCachedBrightnessInfo.brightness.value); - pw.println(" mCachedBrightnessInfo.adjustedBrightness=" - + mCachedBrightnessInfo.adjustedBrightness.value); - pw.println(" mCachedBrightnessInfo.brightnessMin=" - + mCachedBrightnessInfo.brightnessMin.value); - pw.println(" mCachedBrightnessInfo.brightnessMax=" - + mCachedBrightnessInfo.brightnessMax.value); - pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value); - pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" - + mCachedBrightnessInfo.hbmTransitionPoint.value); - pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" - + mCachedBrightnessInfo.brightnessMaxReason.value); - } - pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig); - pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig); - mHandler.runWithScissors(() -> dumpLocal(pw), 1000); - } - - private void dumpLocal(PrintWriter pw) { - pw.println(); - pw.println("Display Power Controller Thread State:"); - pw.println(" mPowerRequest=" + mPowerRequest); - pw.println(" mBrightnessReason=" + mBrightnessReason); - pw.println(" mAppliedDimming=" + mAppliedDimming); - pw.println(" mAppliedThrottling=" + mAppliedThrottling); - pw.println(" mDozing=" + mDozing); - pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState)); - pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime); - pw.println(" mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime); - pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker); - pw.println(" mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker); - pw.println(" mPendingScreenOff=" + mPendingScreenOff); - pw.println(" mReportedToPolicy=" - + reportedToPolicyToString(mReportedScreenStateToPolicy)); - pw.println(" mIsRbcActive=" + mIsRbcActive); - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - mAutomaticBrightnessStrategy.dump(ipw); - - if (mScreenBrightnessRampAnimator != null) { - pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" - + mScreenBrightnessRampAnimator.isAnimating()); - } - - if (mColorFadeOnAnimator != null) { - pw.println(" mColorFadeOnAnimator.isStarted()=" - + mColorFadeOnAnimator.isStarted()); - } - if (mColorFadeOffAnimator != null) { - pw.println(" mColorFadeOffAnimator.isStarted()=" - + mColorFadeOffAnimator.isStarted()); - } - - if (mPowerState != null) { - mPowerState.dump(pw); - } - - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.dump(pw); - dumpBrightnessEvents(pw); - } - - dumpRbcEvents(pw); - - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController.dump(pw); - } - - if (mBrightnessRangeController != null) { - mBrightnessRangeController.dump(pw); - } - - if (mBrightnessThrottler != null) { - mBrightnessThrottler.dump(pw); - } - - pw.println(); - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.dump(pw); - mDisplayWhiteBalanceSettings.dump(pw); - } - - pw.println(); - - if (mWakelockController != null) { - mWakelockController.dumpLocal(pw); - } - - pw.println(); - if (mDisplayBrightnessController != null) { - mDisplayBrightnessController.dump(pw); - } - - pw.println(); - if (mDisplayStateController != null) { - mDisplayStateController.dumpsys(pw); - } - - pw.println(); - if (mBrightnessClamperController != null) { - mBrightnessClamperController.dump(ipw); - } - } - - - private static String reportedToPolicyToString(int state) { - switch (state) { - case REPORTED_TO_POLICY_SCREEN_OFF: - return "REPORTED_TO_POLICY_SCREEN_OFF"; - case REPORTED_TO_POLICY_SCREEN_TURNING_ON: - return "REPORTED_TO_POLICY_SCREEN_TURNING_ON"; - case REPORTED_TO_POLICY_SCREEN_ON: - return "REPORTED_TO_POLICY_SCREEN_ON"; - default: - return Integer.toString(state); - } - } - - private static String skipRampStateToString(int state) { - switch (state) { - case RAMP_STATE_SKIP_NONE: - return "RAMP_STATE_SKIP_NONE"; - case RAMP_STATE_SKIP_INITIAL: - return "RAMP_STATE_SKIP_INITIAL"; - case RAMP_STATE_SKIP_AUTOBRIGHT: - return "RAMP_STATE_SKIP_AUTOBRIGHT"; - default: - return Integer.toString(state); - } - } - - private void dumpBrightnessEvents(PrintWriter pw) { - int size = mBrightnessEventRingBuffer.size(); - if (size < 1) { - pw.println("No Automatic Brightness Adjustments"); - return; - } - - pw.println("Automatic Brightness Adjustments Last " + size + " Events: "); - BrightnessEvent[] eventArray = mBrightnessEventRingBuffer.toArray(); - for (int i = 0; i < mBrightnessEventRingBuffer.size(); i++) { - pw.println(" " + eventArray[i].toString()); - } - } - - private void dumpRbcEvents(PrintWriter pw) { - int size = mRbcEventRingBuffer.size(); - if (size < 1) { - pw.println("No Reduce Bright Colors Adjustments"); - return; - } - - pw.println("Reduce Bright Colors Adjustments Last " + size + " Events: "); - BrightnessEvent[] eventArray = mRbcEventRingBuffer.toArray(); - for (int i = 0; i < mRbcEventRingBuffer.size(); i++) { - pw.println(" " + eventArray[i]); - } - } - - - private void noteScreenState(int screenState) { - // Log screen state change with display id - FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED_V2, - screenState, mDisplayStatsId); - if (mBatteryStats != null) { - try { - // TODO(multi-display): make this multi-display - mBatteryStats.noteScreenState(screenState); - } catch (RemoteException e) { - // same process - } - } - } - - @SuppressLint("AndroidFrameworkRequiresPermission") - private void noteScreenBrightness(float brightness) { - if (mBatteryStats != null) { - try { - // TODO(brightnessfloat): change BatteryStats to use float - int brightnessInt = mFlags.isBrightnessIntRangeUserPerceptionEnabled() - ? BrightnessSynchronizer.brightnessFloatToIntSetting(mContext, brightness) - : BrightnessSynchronizer.brightnessFloatToInt(brightness); - mBatteryStats.noteScreenBrightness(brightnessInt); - } catch (RemoteException e) { - // same process - } - } - } - - private void reportStats(float brightness) { - if (mLastStatsBrightness == brightness) { - return; - } - - float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX; - synchronized (mCachedBrightnessInfo) { - if (mCachedBrightnessInfo.hbmTransitionPoint == null) { - return; - } - hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value; - } - - final boolean aboveTransition = brightness > hbmTransitionPoint; - final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint; - - if (aboveTransition || oldAboveTransition) { - mLastStatsBrightness = brightness; - mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS); - if (aboveTransition != oldAboveTransition) { - // report immediately - logHbmBrightnessStats(brightness, mDisplayStatsId); - } else { - // delay for rate limiting - Message msg = mHandler.obtainMessage(); - msg.what = MSG_STATSD_HBM_BRIGHTNESS; - msg.arg1 = Float.floatToIntBits(brightness); - msg.arg2 = mDisplayStatsId; - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis() - + BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS); - } - } - } - - private void logHbmBrightnessStats(float brightness, int displayStatsId) { - synchronized (mHandler) { - FrameworkStatsLog.write( - FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness); - } - } - - // Return bucket index of range_[left]_[right] where - // left <= nits < right - private int nitsToRangeIndex(float nits) { - for (int i = 0; i < BRIGHTNESS_RANGE_BOUNDARIES.length; i++) { - if (nits < BRIGHTNESS_RANGE_BOUNDARIES[i]) { - return BRIGHTNESS_RANGE_INDEX[i]; - } - } - return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_3000_INF; - } - - private int convertBrightnessReasonToStatsEnum(int brightnessReason) { - switch(brightnessReason) { - case BrightnessReason.REASON_UNKNOWN: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN; - case BrightnessReason.REASON_MANUAL: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_MANUAL; - case BrightnessReason.REASON_DOZE: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_DOZE; - case BrightnessReason.REASON_DOZE_DEFAULT: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_DOZE_DEFAULT; - case BrightnessReason.REASON_AUTOMATIC: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_AUTOMATIC; - case BrightnessReason.REASON_SCREEN_OFF: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_SCREEN_OFF; - case BrightnessReason.REASON_OVERRIDE: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_OVERRIDE; - case BrightnessReason.REASON_TEMPORARY: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_TEMPORARY; - case BrightnessReason.REASON_BOOST: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_BOOST; - case BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_SCREEN_OFF_BRIGHTNESS_SENSOR; - case BrightnessReason.REASON_FOLLOWER: - return FrameworkStatsLog - .DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_FOLLOWER; - } - return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN; - } - - private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness) { - int modifier = event.getReason().getModifier(); - int flags = event.getFlags(); - // It's easier to check if the brightness is at maximum level using the brightness - // value untouched by any modifiers - boolean brightnessIsMax = unmodifiedBrightness == event.getHbmMax(); - float brightnessInNits = - mDisplayBrightnessController.convertToAdjustedNits(event.getBrightness()); - float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f; - int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1; - float appliedHbmMaxNits = - event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF - ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getHbmMax()); - // thermalCapNits set to -1 if not currently capping max brightness - float appliedThermalCapNits = - event.getThermalMax() == PowerManager.BRIGHTNESS_MAX - ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getThermalMax()); - if (mIsDisplayInternal) { - FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED, - mDisplayBrightnessController - .convertToAdjustedNits(event.getInitialBrightness()), - brightnessInNits, - event.getLux(), - event.getPhysicalDisplayId(), - event.wasShortTermModelActive(), - appliedLowPowerMode, - appliedRbcStrength, - appliedHbmMaxNits, - appliedThermalCapNits, - event.isAutomaticBrightnessEnabled(), - FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL, - convertBrightnessReasonToStatsEnum(event.getReason().getReason()), - nitsToRangeIndex(brightnessInNits), - brightnessIsMax, - event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, - event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, - (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0, - mBrightnessClamperController.getBrightnessMaxReason(), - // TODO: (flc) add brightnessMinReason here too. - (modifier & BrightnessReason.MODIFIER_DIMMED) > 0, - event.isRbcEnabled(), - (flags & BrightnessEvent.FLAG_INVALID_LUX) > 0, - (flags & BrightnessEvent.FLAG_DOZE_SCALE) > 0, - (flags & BrightnessEvent.FLAG_USER_SET) > 0, - (flags & BrightnessEvent.FLAG_IDLE_CURVE) > 0, - (flags & BrightnessEvent.FLAG_LOW_POWER_MODE) > 0); - } - } - - /** - * Indicates whether the display state is ready to update. If this is the default display, we - * want to update it right away so that we can draw the boot animation on it. If it is not - * the default display, drawing the boot animation on it would look incorrect, so we need - * to wait until boot is completed. - * @return True if the display state is ready to update - */ - private boolean readyToUpdateDisplayState() { - return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted; - } - - private final class DisplayControllerHandler extends Handler { - DisplayControllerHandler(Looper looper) { - super(looper, null, true /*async*/); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE_POWER_STATE: - updatePowerState(); - break; - - case MSG_SCREEN_ON_UNBLOCKED: - if (mPendingScreenOnUnblocker == msg.obj) { - unblockScreenOn(); - updatePowerState(); - } - break; - case MSG_SCREEN_OFF_UNBLOCKED: - if (mPendingScreenOffUnblocker == msg.obj) { - unblockScreenOff(); - updatePowerState(); - } - break; - case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED: - if (mDisplayOffloadSession == msg.obj) { - unblockScreenOnByDisplayOffload(); - updatePowerState(); - } - break; - case MSG_CONFIGURE_BRIGHTNESS: - BrightnessConfiguration brightnessConfiguration = - (BrightnessConfiguration) msg.obj; - mAutomaticBrightnessStrategy.setBrightnessConfiguration(brightnessConfiguration, - msg.arg1 == 1); - if (mBrightnessTracker != null) { - mBrightnessTracker - .setShouldCollectColorSample(brightnessConfiguration != null - && brightnessConfiguration.shouldCollectColorSamples()); - } - updatePowerState(); - break; - - case MSG_SET_TEMPORARY_BRIGHTNESS: - // TODO: Should we have a a timeout for the temporary brightness? - mDisplayBrightnessController - .setTemporaryBrightness(Float.intBitsToFloat(msg.arg1)); - updatePowerState(); - break; - - case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT: - mAutomaticBrightnessStrategy - .setTemporaryAutoBrightnessAdjustment(Float.intBitsToFloat(msg.arg1)); - updatePowerState(); - break; - - case MSG_STOP: - cleanupHandlerThreadAfterStop(); - break; - - case MSG_UPDATE_BRIGHTNESS: - if (mStopped) { - return; - } - handleSettingsChange(false /*userSwitch*/); - break; - - case MSG_UPDATE_RBC: - handleRbcChanged(); - break; - - case MSG_BRIGHTNESS_RAMP_DONE: - if (mPowerState != null) { - final float brightness = mPowerState.getScreenBrightness(); - reportStats(brightness); - } - break; - - case MSG_STATSD_HBM_BRIGHTNESS: - logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2); - break; - - case MSG_SWITCH_USER: - handleOnSwitchUser(msg.arg1); - break; - - case MSG_BOOT_COMPLETED: - mBootCompleted = true; - updatePowerState(); - break; - - case MSG_SET_DWBC_STRONG_MODE: - setDwbcStrongMode(msg.arg1); - break; - - case MSG_SET_DWBC_COLOR_OVERRIDE: - final float cct = Float.intBitsToFloat(msg.arg1); - setDwbcOverride(cct); - break; - - case MSG_SET_DWBC_LOGGING_ENABLED: - setDwbcLoggingEnabled(msg.arg1); - break; - case MSG_SET_BRIGHTNESS_FROM_OFFLOAD: - mDisplayBrightnessController.setBrightnessFromOffload( - Float.intBitsToFloat(msg.arg1)); - updatePowerState(); - break; - } - } - } - - - private final class SettingsObserver extends ContentObserver { - SettingsObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) { - handleBrightnessModeChange(); - } else if (uri.equals(Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) { - int preset = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, - Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, - UserHandle.USER_CURRENT); - Slog.i(mTag, "Setting up auto-brightness for preset " - + autoBrightnessPresetToString(preset)); - setUpAutoBrightness(mContext, mHandler); - sendUpdatePowerState(); - } else { - handleSettingsChange(false /* userSwitch */); - } - } - } - - private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener { - @Override - public void onScreenOn() { - Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - } - - private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener { - @Override - public void onScreenOff() { - Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this); - mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - } - } - - @Override - public void setAutoBrightnessLoggingEnabled(boolean enabled) { - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.setLoggingEnabled(enabled); - } - } - - @Override // DisplayWhiteBalanceController.Callbacks - public void updateWhiteBalance() { - sendUpdatePowerState(); - } - - @Override - public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) { - Message msg = mHandler.obtainMessage(); - msg.what = MSG_SET_DWBC_LOGGING_ENABLED; - msg.arg1 = enabled ? 1 : 0; - msg.sendToTarget(); - } - - @Override - public void setAmbientColorTemperatureOverride(float cct) { - Message msg = mHandler.obtainMessage(); - msg.what = MSG_SET_DWBC_COLOR_OVERRIDE; - msg.arg1 = Float.floatToIntBits(cct); - msg.sendToTarget(); - } - - /** Functional interface for providing time. */ - @VisibleForTesting - interface Clock { - /** - * Returns current time in milliseconds since boot, not counting time spent in deep sleep. - */ - long uptimeMillis(); - } - - @VisibleForTesting - static class Injector { - Clock getClock() { - return SystemClock::uptimeMillis; - } - - DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, - int displayId, int displayState) { - return new DisplayPowerState(blanker, colorFade, displayId, displayState); - } - - DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, - FloatProperty<DisplayPowerState> firstProperty, - FloatProperty<DisplayPowerState> secondProperty) { - return new DualRampAnimator(dps, firstProperty, secondProperty); - } - - WakelockController getWakelockController(int displayId, - DisplayPowerCallbacks displayPowerCallbacks) { - return new WakelockController(displayId, displayPowerCallbacks); - } - - DisplayPowerProximityStateController getDisplayPowerProximityStateController( - WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, - Looper looper, Runnable nudgeUpdatePowerState, - int displayId, SensorManager sensorManager) { - return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig, - looper, nudgeUpdatePowerState, - displayId, sensorManager, /* injector= */ null); - } - - AutomaticBrightnessController getAutomaticBrightnessController( - AutomaticBrightnessController.Callbacks callbacks, Looper looper, - SensorManager sensorManager, Sensor lightSensor, - SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, - int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, - float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, - long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, - long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle, - boolean resetAmbientLuxAfterWarmUpConfig, - HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, - HysteresisLevels ambientBrightnessThresholdsIdle, - HysteresisLevels screenBrightnessThresholdsIdle, Context context, - BrightnessRangeController brightnessModeController, - BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userNits) { - return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, - brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, - brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, - brighteningLightDebounceConfig, darkeningLightDebounceConfig, - brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle, - resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, - screenBrightnessThresholds, ambientBrightnessThresholdsIdle, - screenBrightnessThresholdsIdle, context, brightnessModeController, - brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, - userNits); - } - - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, - DisplayDeviceConfig displayDeviceConfig, - DisplayWhiteBalanceController displayWhiteBalanceController) { - return BrightnessMappingStrategy.create(context, displayDeviceConfig, - AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController); - } - - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold) { - return new HysteresisLevels(brighteningThresholdsPercentages, - darkeningThresholdsPercentages, brighteningThresholdLevels, - darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold); - } - - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold, boolean potentialOldBrightnessRange) { - return new HysteresisLevels(brighteningThresholdsPercentages, - darkeningThresholdsPercentages, brighteningThresholdLevels, - darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold, - potentialOldBrightnessRange); - } - - ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController( - SensorManager sensorManager, - Sensor lightSensor, - Handler handler, - ScreenOffBrightnessSensorController.Clock clock, - int[] sensorValueToLux, - BrightnessMappingStrategy brightnessMapper) { - return new ScreenOffBrightnessSensorController( - sensorManager, - lightSensor, - handler, - clock, - sensorValueToLux, - brightnessMapper - ); - } - - HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, - int height, IBinder displayToken, String displayUniqueId, float brightnessMin, - float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, - HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, - Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, - Context context) { - return new HighBrightnessModeController(handler, width, height, displayToken, - displayUniqueId, brightnessMin, brightnessMax, hbmData, hdrBrightnessCfg, - hbmChangeCallback, hbmMetadata, context); - } - - BrightnessRangeController getBrightnessRangeController( - HighBrightnessModeController hbmController, Runnable modeChangeCallback, - DisplayDeviceConfig displayDeviceConfig, Handler handler, - DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { - return new BrightnessRangeController(hbmController, - modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info); - } - - BrightnessClamperController getBrightnessClamperController(Handler handler, - BrightnessClamperController.ClamperChangeListener clamperChangeListener, - BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - - return new BrightnessClamperController(handler, clamperChangeListener, data, context, - flags); - } - - DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, - SensorManager sensorManager, Resources resources) { - return DisplayWhiteBalanceFactory.create(handler, - sensorManager, resources); - } - - boolean isColorFadeEnabled() { - return !ActivityManager.isLowRamDeviceStatic(); - } - } - - static class CachedBrightnessInfo { - public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableFloat adjustedBrightness = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableFloat brightnessMin = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableFloat brightnessMax = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); - public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); - public MutableFloat hbmTransitionPoint = - new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID); - public MutableInt brightnessMaxReason = - new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); - - public boolean checkAndSetFloat(MutableFloat mf, float f) { - if (mf.value != f) { - mf.value = f; - return true; - } - return false; - } - - public boolean checkAndSetInt(MutableInt mi, int i) { - if (mi.value != i) { - mi.value = i; - return true; - } - return false; - } - } -} diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java index 465584c3d90c..403dfbe920ee 100644 --- a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java +++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java @@ -41,13 +41,6 @@ public class DeviceConfigParameterProvider { mDeviceConfig = deviceConfig; } - // feature: revamping_display_power_controller_feature - // parameter: use_newly_structured_display_power_controller - public boolean isNewPowerControllerFeatureEnabled() { - return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, - DisplayManager.DeviceConfig.KEY_NEW_POWER_CONTROLLER, true); - } - // feature: hdr_output_control // parameter: enable_hdr_output_control public boolean isHdrOutputControlFeatureEnabled() { diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index 7e990c6c2f4a..380106ba486d 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -104,10 +104,13 @@ public abstract class InputManagerInternal { public abstract PointF getCursorPosition(); /** - * Sets the pointer acceleration. - * See {@code frameworks/native/include/input/VelocityControl.h#VelocityControlParameters}. + * Enables or disables pointer acceleration for mouse movements. + * + * Note that this only affects pointer movements from mice (that is, pointing devices which send + * relative motions, including trackballs and pointing sticks), not from other pointer devices + * such as touchpads and styluses. */ - public abstract void setPointerAcceleration(float acceleration, int displayId); + public abstract void setMousePointerAccelerationEnabled(boolean enabled, int displayId); /** * Sets the eligibility of windows on a given display for pointer capture. If a display is diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 36dac8316e54..bc7205ae2c07 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -64,7 +64,6 @@ import android.os.CombinedVibration; import android.os.Environment; import android.os.Handler; import android.os.IBinder; -import android.os.IInputConstants; import android.os.IVibratorStateListener; import android.os.InputEventInjectionResult; import android.os.InputEventInjectionSync; @@ -1372,9 +1371,9 @@ public class InputManagerService extends IInputManager.Stub mNative.setPointerSpeed(speed); } - private void setPointerAcceleration(float acceleration, int displayId) { + private void setMousePointerAccelerationEnabled(boolean enabled, int displayId) { updateAdditionalDisplayInputProperties(displayId, - properties -> properties.pointerAcceleration = acceleration); + properties -> properties.mousePointerAccelerationEnabled = enabled); } private void setPointerIconVisible(boolean visible, int displayId) { @@ -2261,7 +2260,8 @@ public class InputManagerService extends IInputManager.Stub + mAdditionalDisplayInputProperties.keyAt(i)); final AdditionalDisplayInputProperties properties = mAdditionalDisplayInputProperties.valueAt(i); - pw.println("pointerAcceleration: " + properties.pointerAcceleration); + pw.println("mousePointerAccelerationEnabled: " + + properties.mousePointerAccelerationEnabled); pw.println("pointerIconVisible: " + properties.pointerIconVisible); } pw.decreaseIndent(); @@ -3289,8 +3289,8 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void setPointerAcceleration(float acceleration, int displayId) { - InputManagerService.this.setPointerAcceleration(acceleration, displayId); + public void setMousePointerAccelerationEnabled(boolean enabled, int displayId) { + InputManagerService.this.setMousePointerAccelerationEnabled(enabled, displayId); } @Override @@ -3382,11 +3382,15 @@ public class InputManagerService extends IInputManager.Stub private static class AdditionalDisplayInputProperties { static final boolean DEFAULT_POINTER_ICON_VISIBLE = true; - static final float DEFAULT_POINTER_ACCELERATION = - (float) IInputConstants.DEFAULT_POINTER_ACCELERATION; + static final boolean DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED = true; - // The pointer acceleration for this display. - public float pointerAcceleration; + /** + * Whether to enable mouse pointer acceleration on this display. Note that this only affects + * pointer movements from mice (that is, pointing devices which send relative motions, + * including trackballs and pointing sticks), not from other pointer devices such as + * touchpads and styluses. + */ + public boolean mousePointerAccelerationEnabled; // Whether the pointer icon should be visible or hidden on this display. public boolean pointerIconVisible; @@ -3396,12 +3400,12 @@ public class InputManagerService extends IInputManager.Stub } public boolean allDefaults() { - return Float.compare(pointerAcceleration, DEFAULT_POINTER_ACCELERATION) == 0 + return mousePointerAccelerationEnabled == DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED && pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE; } public void reset() { - pointerAcceleration = DEFAULT_POINTER_ACCELERATION; + mousePointerAccelerationEnabled = DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED; pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE; } } @@ -3433,9 +3437,11 @@ public class InputManagerService extends IInputManager.Stub } } - if (properties.pointerAcceleration != mCurrentDisplayProperties.pointerAcceleration) { - mCurrentDisplayProperties.pointerAcceleration = properties.pointerAcceleration; - mNative.setPointerAcceleration(properties.pointerAcceleration); + if (properties.mousePointerAccelerationEnabled + != mCurrentDisplayProperties.mousePointerAccelerationEnabled) { + mCurrentDisplayProperties.mousePointerAccelerationEnabled = + properties.mousePointerAccelerationEnabled; + mNative.setMousePointerAccelerationEnabled(properties.mousePointerAccelerationEnabled); } } diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index 829b6607f113..dd9204cd1401 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -118,7 +118,7 @@ interface NativeInputManagerService { void setPointerSpeed(int speed); - void setPointerAcceleration(float acceleration); + void setMousePointerAccelerationEnabled(boolean enabled); void setTouchpadPointerSpeed(int speed); @@ -351,7 +351,7 @@ interface NativeInputManagerService { public native void setPointerSpeed(int speed); @Override - public native void setPointerAcceleration(float acceleration); + public native void setMousePointerAccelerationEnabled(boolean enabled); @Override public native void setTouchpadPointerSpeed(int speed); diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS index aa638aa49fb3..e507c6ba40a1 100644 --- a/services/core/java/com/android/server/inputmethod/OWNERS +++ b/services/core/java/com/android/server/inputmethod/OWNERS @@ -6,5 +6,8 @@ tarandeep@google.com fstern@google.com cosminbaies@google.com +# Automotive +kanant@google.com + ogunwale@google.com #{LAST_RESORT_SUGGESTION} jjaggi@google.com #{LAST_RESORT_SUGGESTION} diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index d02b6f4cff53..171fbb6f8a16 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -25,6 +25,7 @@ import static com.android.server.location.gnss.GnssManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; +import android.location.GnssMeasurement; import android.location.GnssMeasurementRequest; import android.location.GnssMeasurementsEvent; import android.location.IGnssMeasurementsListener; @@ -33,6 +34,7 @@ import android.os.IBinder; import android.stats.location.LocationStatsEnums; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.server.location.gnss.GnssConfiguration.HalInterfaceVersion; import com.android.server.location.gnss.hal.GnssNative; import com.android.server.location.injector.AppOpsHelper; @@ -40,6 +42,8 @@ import com.android.server.location.injector.Injector; import com.android.server.location.injector.LocationUsageLogger; import com.android.server.location.injector.SettingsHelper; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.Collection; /** @@ -91,6 +95,9 @@ public final class GnssMeasurementsProvider extends private final LocationUsageLogger mLogger; private final GnssNative mGnssNative; + @GuardedBy("mMultiplexerLock") + private GnssMeasurementsEvent mLastGnssMeasurementsEvent; + public GnssMeasurementsProvider(Injector injector, GnssNative gnssNative) { super(injector); mAppOpsHelper = injector.getAppOpsHelper(); @@ -264,5 +271,46 @@ public final class GnssMeasurementsProvider extends return null; } }); + synchronized (mMultiplexerLock) { + mLastGnssMeasurementsEvent = event; + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.print("last measurements="); + pw.println(getLastMeasurementEventSummary()); + } + + /** + * Returns a string of GnssMeasurementsEvent summary including received time, satellite count + * and average baseband C/No. + */ + private String getLastMeasurementEventSummary() { + synchronized (mMultiplexerLock) { + if (mLastGnssMeasurementsEvent == null) { + return null; + } + StringBuilder builder = new StringBuilder("["); + builder.append("elapsedRealtimeNs=").append( + mLastGnssMeasurementsEvent.getClock().getElapsedRealtimeNanos()); + builder.append(" measurementCount=").append( + mLastGnssMeasurementsEvent.getMeasurements().size()); + + float sumBasebandCn0 = 0; + int countBasebandCn0 = 0; + for (GnssMeasurement measurement : mLastGnssMeasurementsEvent.getMeasurements()) { + if (measurement.hasBasebandCn0DbHz()) { + sumBasebandCn0 += measurement.getBasebandCn0DbHz(); + countBasebandCn0++; + } + } + if (countBasebandCn0 > 0) { + builder.append(" avgBasebandCn0=").append(sumBasebandCn0 / countBasebandCn0); + } + builder.append("]"); + return builder.toString(); + } } } diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java index c772e08b5f9f..5df0de83b567 100644 --- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java @@ -22,12 +22,14 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.os.SystemClock; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.flags.Flags; import com.android.server.FgThread; import java.util.Objects; @@ -104,10 +106,26 @@ public class SystemEmergencyHelper extends EmergencyHelper { boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE && (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs; - return mIsInEmergencyCall - || isInExtensionTime - || mTelephonyManager.getEmergencyCallbackMode() - || mTelephonyManager.isInEmergencySmsMode(); + if (!Flags.enforceTelephonyFeatureMapping()) { + return mIsInEmergencyCall + || isInExtensionTime + || mTelephonyManager.getEmergencyCallbackMode() + || mTelephonyManager.isInEmergencySmsMode(); + } else { + boolean emergencyCallbackMode = false; + boolean emergencySmsMode = false; + PackageManager pm = mContext.getPackageManager(); + if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) { + emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode(); + } + if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) { + emergencySmsMode = mTelephonyManager.isInEmergencySmsMode(); + } + return mIsInEmergencyCall + || isInExtensionTime + || emergencyCallbackMode + || emergencySmsMode; + } } private class EmergencyCallTelephonyCallback extends TelephonyCallback implements diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ad090829a2f6..542b3b06184a 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -52,6 +52,7 @@ import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYP import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IActivityManager; @@ -74,6 +75,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; @@ -303,7 +305,7 @@ public class LockSettingsService extends ILockSettings.Stub { private boolean mThirdPartyAppsStarted; // Current password metrics for all secured users on the device. Updated when user unlocks the - // device or changes password. Removed when user is stopped. + // device or changes password. Removed if user is stopped with its CE key evicted. @GuardedBy("this") private final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>(); @VisibleForTesting @@ -793,13 +795,33 @@ public class LockSettingsService extends ILockSettings.Stub { } @VisibleForTesting + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.QUERY_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) void onUserStopped(int userId) { hideEncryptionNotification(new UserHandle(userId)); - // User is stopped with its CE key evicted. Restore strong auth requirement to the default - // flags after boot since stopping and restarting a user later is equivalent to rebooting - // the device. + + // Normally, CE storage is locked when a user is stopped, and restarting the user requires + // strong auth. Therefore, reset the user's strong auth flags. The exception is users that + // allow delayed locking; under some circumstances, biometric authentication is allowed to + // restart such users. Don't reset the strong auth flags for such users. + // + // TODO(b/319142556): It might make more sense to reset the strong auth flags when CE + // storage is locked, instead of when the user is stopped. This would ensure the flags get + // reset if CE storage is locked later for a user that allows delayed locking. + if (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) { + UserProperties userProperties = mUserManager.getUserProperties(UserHandle.of(userId)); + if (userProperties != null && userProperties.getAllowStoppingUserWithDelayedLocking()) { + return; + } + } int strongAuthRequired = LockPatternUtils.StrongAuthTracker.getDefaultFlags(mContext); requireStrongAuth(strongAuthRequired, userId); + + // Don't keep the password metrics in memory for a stopped user that will require strong + // auth to start again, since strong auth will make the password metrics available again. synchronized (this) { mUserPasswordMetrics.remove(userId); } 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 6deda468f9a2..f6571d94d554 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -653,14 +653,14 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public boolean hasProjectionPermission(int uid, String packageName) { + public boolean hasProjectionPermission(int processUid, String packageName) { final long token = Binder.clearCallingIdentity(); boolean hasPermission = false; try { hasPermission |= checkPermission(packageName, android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) || mAppOps.noteOpNoThrow( - AppOpsManager.OP_PROJECT_MEDIA, uid, packageName) + AppOpsManager.OP_PROJECT_MEDIA, processUid, packageName) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(token); @@ -669,7 +669,7 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public IMediaProjection createProjection(int uid, String packageName, int type, + public IMediaProjection createProjection(int processUid, String packageName, int type, boolean isPermanentGrant) { if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { @@ -680,13 +680,13 @@ public final class MediaProjectionManagerService extends SystemService throw new IllegalArgumentException("package name must not be empty"); } final UserHandle callingUser = Binder.getCallingUserHandle(); - return createProjectionInternal(uid, packageName, type, isPermanentGrant, + return createProjectionInternal(processUid, packageName, type, isPermanentGrant, callingUser); } @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public IMediaProjection getProjection(int uid, String packageName) { + public IMediaProjection getProjection(int processUid, String packageName) { getProjection_enforcePermission(); if (packageName == null || packageName.isEmpty()) { throw new IllegalArgumentException("package name must not be empty"); @@ -695,7 +695,7 @@ public final class MediaProjectionManagerService extends SystemService MediaProjection projection; final long callingToken = Binder.clearCallingIdentity(); try { - projection = getProjectionInternal(uid, packageName); + projection = getProjectionInternal(processUid, packageName); } finally { Binder.restoreCallingIdentity(callingToken); } @@ -869,12 +869,13 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) { + public void notifyPermissionRequestInitiated( + int hostProcessUid, int sessionCreationSource) { notifyPermissionRequestInitiated_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { MediaProjectionManagerService.this.notifyPermissionRequestInitiated( - hostUid, sessionCreationSource); + hostProcessUid, sessionCreationSource); } finally { Binder.restoreCallingIdentity(token); } @@ -882,11 +883,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestDisplayed(int hostUid) { + public void notifyPermissionRequestDisplayed(int hostProcessUid) { notifyPermissionRequestDisplayed_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostUid); + MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -894,11 +895,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyPermissionRequestCancelled(int hostUid) { + public void notifyPermissionRequestCancelled(int hostProcessUid) { notifyPermissionRequestCancelled_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid); + MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -906,11 +907,11 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void notifyAppSelectorDisplayed(int hostUid) { + public void notifyAppSelectorDisplayed(int hostProcessUid) { notifyAppSelectorDisplayed_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { - MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostUid); + MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostProcessUid); } finally { Binder.restoreCallingIdentity(token); } @@ -919,12 +920,12 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call @EnforcePermission(MANAGE_MEDIA_PROJECTION) public void notifyWindowingModeChanged( - int contentToRecord, int targetUid, int windowingMode) { + int contentToRecord, int targetProcessUid, int windowingMode) { notifyWindowingModeChanged_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { MediaProjectionManagerService.this.notifyWindowingModeChanged( - contentToRecord, targetUid, windowingMode); + contentToRecord, targetProcessUid, windowingMode); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java index e3880c383632..deb95d8a392d 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java @@ -108,7 +108,7 @@ public class NotificationHistoryManager { for (int i = 0; i < pendingPackageRemovals.size(); i++) { userHistory.onPackageRemoved(pendingPackageRemovals.get(i)); } - mUserPendingPackageRemovals.put(userId, null); + mUserPendingPackageRemovals.remove(userId); } // delete history if it was disabled when the user was locked @@ -133,7 +133,7 @@ public class NotificationHistoryManager { synchronized (mLock) { // Actual data deletion is handled by other parts of the system (the entire directory is // removed) - we just need clean up our internal state for GC - mUserPendingPackageRemovals.put(userId, null); + mUserPendingPackageRemovals.remove(userId); mHistoryEnabled.put(userId, false); mUserPendingHistoryDisables.put(userId, false); onUserStopped(userId); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index dd9541e5dda1..92be4ee3acd9 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -698,7 +698,7 @@ final class InstallPackageHelper { pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId); pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId); // Clear any existing archive state. - pkgSetting.setArchiveState(null, userId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(packageName, userId); mPm.mSettings.writePackageRestrictionsLPr(userId); mPm.mSettings.writeKernelMappingLPr(pkgSetting); installed = true; @@ -2281,7 +2281,7 @@ final class InstallPackageHelper { installerPackageName); } // Clear any existing archive state. - ps.setArchiveState(null, userId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, userId); } else if (allUsers != null) { // The caller explicitly specified INSTALL_ALL_USERS flag. // Thus, updating the settings to install the app for all users. @@ -2305,7 +2305,8 @@ final class InstallPackageHelper { installerPackageName); } // Clear any existing archive state. - ps.setArchiveState(null, currentUserId); + mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, + currentUserId); } else { ps.setInstalled(false, currentUserId); } diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index a4af5e71000c..3e5759a88213 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -71,6 +71,7 @@ import android.graphics.drawable.LayerDrawable; import android.os.Binder; import android.os.Bundle; import android.os.Environment; +import android.os.FileUtils; import android.os.IBinder; import android.os.ParcelableException; import android.os.Process; @@ -309,6 +310,26 @@ public class PackageArchiver { return false; } + void clearArchiveState(String packageName, int userId) { + synchronized (mPm.mLock) { + PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + ps.setArchiveState(/* archiveState= */ null, userId); + } + } + mPm.mBackgroundHandler.post( + () -> { + File iconsDir = getIconsDir(packageName, userId); + if (!iconsDir.exists()) { + return; + } + // TODO(b/319238030) Move this into installd. + if (!FileUtils.deleteContentsAndDir(iconsDir)) { + Slog.e(TAG, "Failed to clean up archive files for " + packageName); + } + }); + } + @Nullable private String getCurrentLauncherPackageName(int userId) { ComponentName defaultLauncherComponent = mPm.snapshotComputer().getDefaultHomeActivity( @@ -437,8 +458,8 @@ public class PackageArchiver { if (mainActivity.iconBitmap == null) { return null; } - File iconsDir = createIconsDir(userId); - File iconFile = new File(iconsDir, packageName + "-" + index + ".png"); + File iconsDir = createIconsDir(packageName, userId); + File iconFile = new File(iconsDir, index + ".png"); try (FileOutputStream out = new FileOutputStream(iconFile)) { out.write(mainActivity.iconBitmap); out.flush(); @@ -454,14 +475,14 @@ public class PackageArchiver { // The app doesn't define an icon. No need to store anything. return null; } - File iconsDir = createIconsDir(userId); - File iconFile = new File(iconsDir, packageName + "-" + index + ".png"); + File iconsDir = createIconsDir(packageName, userId); + File iconFile = new File(iconsDir, index + ".png"); Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0), iconSize); try (FileOutputStream out = new FileOutputStream(iconFile)) { // Note: Quality is ignored for PNGs. if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) { throw new IOException(TextUtils.formatSimple("Failure to store icon file %s", - iconFile.getName())); + iconFile.getAbsolutePath())); } out.flush(); } @@ -793,8 +814,20 @@ public class PackageArchiver { } @VisibleForTesting - Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) { - return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString()); + @Nullable + Bitmap decodeIcon(ArchiveActivityInfo activityInfo) { + Path iconBitmap = activityInfo.getIconBitmap(); + if (iconBitmap == null) { + return null; + } + Bitmap bitmap = BitmapFactory.decodeFile(iconBitmap.toString()); + // TODO(b/278553670) We should throw here after some time. Failing graciously now because + // we've just changed the place where we store icons. + if (bitmap == null) { + Slog.e(TAG, "Archived icon cannot be decoded " + iconBitmap.toAbsolutePath()); + return null; + } + return bitmap; } Bitmap includeCloudOverlay(Bitmap bitmap) { @@ -1075,8 +1108,9 @@ public class PackageArchiver { } } - private static File createIconsDir(@UserIdInt int userId) throws IOException { - File iconsDir = getIconsDir(userId); + private static File createIconsDir(String packageName, @UserIdInt int userId) + throws IOException { + File iconsDir = getIconsDir(packageName, userId); if (!iconsDir.isDirectory()) { iconsDir.delete(); iconsDir.mkdirs(); @@ -1088,8 +1122,10 @@ public class PackageArchiver { return iconsDir; } - private static File getIconsDir(int userId) { - return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR); + private static File getIconsDir(String packageName, int userId) { + return new File( + new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR), + packageName); } private static byte[] bytesFromBitmapFile(Path path) throws IOException { @@ -1195,7 +1231,7 @@ public class PackageArchiver { UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); if (extraIntent != null && user != null && mAppStateHelper.isAppTopVisible( - getCurrentLauncherPackageName(user.getIdentifier()))) { + getCurrentLauncherPackageName(user.getIdentifier()))) { extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(extraIntent, user); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index a9118d46b4ba..fdcd28b0ed02 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -25,6 +25,7 @@ import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVI import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED; import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR; import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; +import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; @@ -1405,7 +1406,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, statusReceiver, versionedPackage.getPackageName(), - canSilentlyInstallPackage, userId); + canSilentlyInstallPackage, userId, mPackageArchiver, flags); final boolean shouldShowConfirmationDialog = (flags & PackageManager.DELETE_SHOW_DIALOG) != 0; if (!shouldShowConfirmationDialog @@ -1759,7 +1760,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements binderUid, unarchiveId)); } - session.reportUnarchivalStatus(unarchiveId, status, requiredStorageBytes, + session.reportUnarchivalStatus(status, unarchiveId, requiredStorageBytes, userActionIntent); } } @@ -1828,9 +1829,23 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final IntentSender mTarget; private final String mPackageName; private final Notification mNotification; + private final int mUserId; - public PackageDeleteObserverAdapter(Context context, IntentSender target, + @DeleteFlags + private final int mFlags; + + @Nullable + private final PackageArchiver mPackageArchiver; + + PackageDeleteObserverAdapter(Context context, IntentSender target, String packageName, boolean showNotification, int userId) { + this(context, target, packageName, showNotification, userId, + /* packageArchiver= */ null, /* flags= */ 0); + } + + PackageDeleteObserverAdapter(Context context, IntentSender target, + String packageName, boolean showNotification, int userId, + PackageArchiver packageArchiver, @DeleteFlags int flags) { mContext = context; mTarget = target; mPackageName = packageName; @@ -1842,6 +1857,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } else { mNotification = null; } + mUserId = userId; + mPackageArchiver = packageArchiver; + mFlags = flags; } private String getDeviceOwnerDeletedPackageMsg() { @@ -1883,6 +1901,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements SystemMessage.NOTE_PACKAGE_STATE, mNotification); } + if (mPackageArchiver != null + && PackageManager.DELETE_SUCCEEDED != returnCode + && (mFlags & DELETE_ARCHIVE) != 0) { + mPackageArchiver.clearArchiveState(mPackageName, mUserId); + } if (mTarget == null) { return; } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 3adeb4b5925f..446c6293aa35 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -77,6 +77,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.LocaleList; import android.os.Looper; @@ -113,7 +114,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.infra.AndroidFuture; import com.android.internal.logging.MetricsLogger; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; @@ -485,7 +485,14 @@ public class ShortcutService extends IShortcutService.Stub { } public ShortcutService(Context context) { - this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); + this(context, getBgLooper(), /*onyForPackgeManagerApis*/ false); + } + + private static Looper getBgLooper() { + final HandlerThread handlerThread = new HandlerThread("shortcut", + android.os.Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + return handlerThread.getLooper(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 49af4fedb643..c1b74898e5ae 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2337,8 +2337,18 @@ public class UserManagerService extends IUserManager.Stub { final long identity = Binder.clearCallingIdentity(); try { final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); - if (telecomManager != null && telecomManager.isInCall()) { - flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; + if (com.android.internal.telephony.flags + .Flags.enforceTelephonyFeatureMappingForPublicApis()) { + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELECOM)) { + if (telecomManager != null && telecomManager.isInCall()) { + flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; + } + } + } else { + if (telecomManager != null && telecomManager.isInCall()) { + flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; + } } } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 8d934089524c..13f114138261 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -1279,8 +1279,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba ipw.println(); } - PackageWatchdog.getInstance(mContext).dump(ipw); }); + PackageWatchdog.getInstance(mContext).dump(ipw); } @AnyThread diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 58acbe08acf1..c0e33084f9c4 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -1339,16 +1339,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { String inputId = mHardwareInputIdMap.get(deviceId); if (inputId != null) { - if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) { - if (previousCableConnectionStatus != connection.getInputStateLocked()) { - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - connection.getInputStateLocked(), 0, inputId).sendToTarget(); - } - } else { - if ((previousConfigsLength == 0) - != (connection.getConfigsLengthLocked() == 0)) { - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - connection.getInputStateLocked(), 0, inputId).sendToTarget(); + synchronized (mLock) { + if (connection.updateCableConnectionStatusLocked( + cableConnectionStatus)) { + if (previousCableConnectionStatus + != connection.getInputStateLocked()) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId) + .sendToTarget(); + } + } else { + if ((previousConfigsLength == 0) + != (connection.getConfigsLengthLocked() == 0)) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId) + .sendToTarget(); + } } } } diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index 9213d96ad4ca..ed04e5fde024 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -34,6 +34,7 @@ public class VcnContext { @NonNull private final Looper mLooper; @NonNull private final VcnNetworkProvider mVcnNetworkProvider; @NonNull private final FeatureFlags mFeatureFlags; + @NonNull private final com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; private final boolean mIsInTestMode; public VcnContext( @@ -48,6 +49,7 @@ public class VcnContext { // Auto-generated class mFeatureFlags = new FeatureFlagsImpl(); + mCoreNetFeatureFlags = new com.android.net.flags.FeatureFlagsImpl(); } @NonNull @@ -69,11 +71,23 @@ public class VcnContext { return mIsInTestMode; } + public boolean isFlagNetworkMetricMonitorEnabled() { + return mFeatureFlags.networkMetricMonitor(); + } + + public boolean isFlagIpSecTransformStateEnabled() { + return mCoreNetFeatureFlags.ipsecTransformState(); + } + @NonNull public FeatureFlags getFeatureFlags() { return mFeatureFlags; } + public boolean isFlagSafeModeTimeoutConfigEnabled() { + return mFeatureFlags.safeModeTimeoutConfig(); + } + /** * Verifies that the caller is running on the VcnContext Thread. * diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 54c97dd37941..fcc0de1c2258 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -915,9 +915,11 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/180132994): explore safely removing this Thread check mVcnContext.ensureRunningOnLooperThread(); - logInfo( - "Selected underlying network changed: " - + (underlying == null ? null : underlying.network)); + if (!UnderlyingNetworkRecord.isSameNetwork(mUnderlying, underlying)) { + logInfo( + "Selected underlying network changed: " + + (underlying == null ? null : underlying.network)); + } // TODO(b/179091925): Move the delayed-message handling to BaseState @@ -1242,9 +1244,28 @@ public class VcnGatewayConnection extends StateMachine { createScheduledAlarm( SAFEMODE_TIMEOUT_ALARM, delayedMessage, - mVcnContext.isInTestMode() - ? TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS_TEST_MODE) - : TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + getSafeModeTimeoutMs(mVcnContext, mLastSnapshot, mSubscriptionGroup)); + } + + /** Gets the safe mode timeout */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static long getSafeModeTimeoutMs( + VcnContext vcnContext, TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { + final int defaultSeconds = + vcnContext.isInTestMode() + ? SAFEMODE_TIMEOUT_SECONDS_TEST_MODE + : SAFEMODE_TIMEOUT_SECONDS; + + final PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); + int resultSeconds = defaultSeconds; + + if (vcnContext.isFlagSafeModeTimeoutConfigEnabled() && carrierConfig != null) { + resultSeconds = + carrierConfig.getInt( + VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, defaultSeconds); + } + + return TimeUnit.SECONDS.toMillis(resultSeconds); } private void cancelSafeModeAlarm() { diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java new file mode 100644 index 000000000000..5f4852f77727 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java @@ -0,0 +1,387 @@ +/* + * 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.server.vcn.routeselection; + +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.IpSecTransformState; +import android.net.Network; +import android.net.vcn.VcnManager; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.BitSet; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * IpSecPacketLossDetector is responsible for continuously monitoring IPsec packet loss + * + * <p>When the packet loss rate surpass the threshold, IpSecPacketLossDetector will report it to the + * caller + * + * <p>IpSecPacketLossDetector will start monitoring when the network being monitored is selected AND + * an inbound IpSecTransform has been applied to this network. + * + * <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state" + */ +public class IpSecPacketLossDetector extends NetworkMetricMonitor { + private static final String TAG = IpSecPacketLossDetector.class.getSimpleName(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PACKET_LOSS_UNAVALAIBLE = -1; + + // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality + // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and + // Security"). For audio and video streaming, above 10-12% packet loss is unacceptable (as per + // "ICTP-SDU: About PingER"). Thus choose 12% as a conservative default threshold to declare a + // validation failure. + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT = 12; + + private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20; + + private long mPollIpSecStateIntervalMs; + private final int mPacketLossRatePercentThreshold; + + @NonNull private final Handler mHandler; + @NonNull private final PowerManager mPowerManager; + @NonNull private final Object mCancellationToken = new Object(); + @NonNull private final PacketLossCalculator mPacketLossCalculator; + + @Nullable private IpSecTransformWrapper mInboundTransform; + @Nullable private IpSecTransformState mLastIpSecTransformState; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback, + @NonNull Dependencies deps) + throws IllegalAccessException { + super(vcnContext, network, carrierConfig, callback); + + Objects.requireNonNull(deps, "Missing deps"); + + if (!vcnContext.isFlagIpSecTransformStateEnabled()) { + // Caller error + logWtf("ipsecTransformState flag disabled"); + throw new IllegalAccessException("ipsecTransformState flag disabled"); + } + + mHandler = new Handler(getVcnContext().getLooper()); + + mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class); + + mPacketLossCalculator = deps.getPacketLossCalculator(); + + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); + + // Register for system broadcasts to monitor idle mode change + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + getVcnContext() + .getContext() + .registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals( + intent.getAction()) + && mPowerManager.isDeviceIdleMode()) { + mLastIpSecTransformState = null; + } + } + }, + intentFilter, + null /* broadcastPermission not required */, + mHandler); + } + + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + this(vcnContext, network, carrierConfig, callback, new Dependencies()); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + public PacketLossCalculator getPacketLossCalculator() { + return new PacketLossCalculator(); + } + } + + private static long getPollIpSecStateIntervalMs( + @Nullable PersistableBundleWrapper carrierConfig) { + final int seconds; + + if (carrierConfig != null) { + seconds = + carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT); + } else { + seconds = POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT; + } + + return TimeUnit.SECONDS.toMillis(seconds); + } + + private static int getPacketLossRatePercentThreshold( + @Nullable PersistableBundleWrapper carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, + IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT); + } + return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT; + } + + @Override + protected void onSelectedUnderlyingNetworkChanged() { + if (!isSelectedUnderlyingNetwork()) { + mInboundTransform = null; + stop(); + } + + // No action when the underlying network got selected. Wait for the inbound transform to + // start the monitor + } + + @Override + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inboundTransform) { + Objects.requireNonNull(inboundTransform, "inboundTransform is null"); + + if (Objects.equals(inboundTransform, mInboundTransform)) { + return; + } + + if (!isSelectedUnderlyingNetwork()) { + logWtf("setInboundTransform called but network not selected"); + return; + } + + // When multiple parallel inbound transforms are created, NetworkMetricMonitor will be + // enabled on the last one as a sample + mInboundTransform = inboundTransform; + start(); + } + + @Override + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // The already scheduled event will not be affected. The followup events will be scheduled + // with the new interval + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + } + + @Override + protected void start() { + super.start(); + clearTransformStateAndPollingEvents(); + mHandler.postDelayed(new PollIpSecStateRunnable(), mCancellationToken, 0L); + } + + @Override + public void stop() { + super.stop(); + clearTransformStateAndPollingEvents(); + } + + private void clearTransformStateAndPollingEvents() { + mHandler.removeCallbacksAndEqualMessages(mCancellationToken); + mLastIpSecTransformState = null; + } + + @Override + public void close() { + super.close(); + + if (mInboundTransform != null) { + mInboundTransform.close(); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @Nullable + public IpSecTransformState getLastTransformState() { + return mLastIpSecTransformState; + } + + @VisibleForTesting(visibility = Visibility.PROTECTED) + @Nullable + public IpSecTransformWrapper getInboundTransformInternal() { + return mInboundTransform; + } + + private class PollIpSecStateRunnable implements Runnable { + @Override + public void run() { + if (!isStarted()) { + logWtf("Monitor stopped but PollIpSecStateRunnable not removed from Handler"); + return; + } + + getInboundTransformInternal() + .getIpSecTransformState( + new HandlerExecutor(mHandler), new IpSecTransformStateReceiver()); + + // Schedule for next poll + mHandler.postDelayed( + new PollIpSecStateRunnable(), mCancellationToken, mPollIpSecStateIntervalMs); + } + } + + private class IpSecTransformStateReceiver + implements OutcomeReceiver<IpSecTransformState, RuntimeException> { + @Override + public void onResult(@NonNull IpSecTransformState state) { + getVcnContext().ensureRunningOnLooperThread(); + + if (!isStarted()) { + return; + } + + onIpSecTransformStateReceived(state); + } + + @Override + public void onError(@NonNull RuntimeException error) { + getVcnContext().ensureRunningOnLooperThread(); + + // Nothing we can do here + logW("TransformStateReceiver#onError " + error.toString()); + } + } + + private void onIpSecTransformStateReceived(@NonNull IpSecTransformState state) { + if (mLastIpSecTransformState == null) { + // This is first time to poll the state + mLastIpSecTransformState = state; + return; + } + + final int packetLossRate = + mPacketLossCalculator.getPacketLossRatePercentage( + mLastIpSecTransformState, state, getLogPrefix()); + + if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) { + return; + } + + final String logMsg = + "packetLossRate: " + + packetLossRate + + "% in the past " + + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp()) + + "ms"; + + mLastIpSecTransformState = state; + if (packetLossRate < mPacketLossRatePercentThreshold) { + logV(logMsg); + onValidationResultReceivedInternal(false /* isFailed */); + } else { + logInfo(logMsg); + onValidationResultReceivedInternal(true /* isFailed */); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class PacketLossCalculator { + /** Calculate the packet loss rate between two timestamps */ + public int getPacketLossRatePercentage( + @NonNull IpSecTransformState oldState, + @NonNull IpSecTransformState newState, + String logPrefix) { + logVIpSecTransform("oldState", oldState, logPrefix); + logVIpSecTransform("newState", newState, logPrefix); + + final int replayWindowSize = oldState.getReplayBitmap().length * 8; + final long oldSeqHi = oldState.getRxHighestSequenceNumber(); + final long oldSeqLow = Math.max(0L, oldSeqHi - replayWindowSize + 1); + final long newSeqHi = newState.getRxHighestSequenceNumber(); + final long newSeqLow = Math.max(0L, newSeqHi - replayWindowSize + 1); + + if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) { + // The replay window did not proceed and all packets might have been delivered out + // of order + return PACKET_LOSS_UNAVALAIBLE; + } + + // Get the expected packet count by assuming there is no packet loss. In this case, SA + // should receive all packets whose sequence numbers are smaller than the lower bound of + // the replay window AND the packets received within the window. + // When the lower bound is 0, it's not possible to tell whether packet with seqNo 0 is + // received or not. For simplicity just assume that packet is received. + final long newExpectedPktCnt = newSeqLow + getPacketCntInReplayWindow(newState); + final long oldExpectedPktCnt = oldSeqLow + getPacketCntInReplayWindow(oldState); + + final long expectedPktCntDiff = newExpectedPktCnt - oldExpectedPktCnt; + final long actualPktCntDiff = newState.getPacketCount() - oldState.getPacketCount(); + + logV( + TAG, + logPrefix + + " expectedPktCntDiff: " + + expectedPktCntDiff + + " actualPktCntDiff: " + + actualPktCntDiff); + + if (expectedPktCntDiff < 0 + || expectedPktCntDiff == 0 + || actualPktCntDiff < 0 + || actualPktCntDiff > expectedPktCntDiff) { + logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff"); + return PACKET_LOSS_UNAVALAIBLE; + } + + return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); + } + } + + private static void logVIpSecTransform( + String transformTag, IpSecTransformState state, String logPrefix) { + final String stateString = + " seqNo: " + + state.getRxHighestSequenceNumber() + + " | pktCnt: " + + state.getPacketCount() + + " | pktCntInWindow: " + + getPacketCntInReplayWindow(state); + logV(TAG, logPrefix + " " + transformTag + stateString); + } + + /** Get the number of received packets within the replay window */ + private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) { + return BitSet.valueOf(state.getReplayBitmap()).cardinality(); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java new file mode 100644 index 000000000000..a79f188713e1 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java @@ -0,0 +1,269 @@ +/* + * 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.server.vcn.routeselection; + +import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IpSecTransform; +import android.net.IpSecTransformState; +import android.net.Network; +import android.os.OutcomeReceiver; +import android.util.CloseGuard; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * NetworkMetricMonitor is responsible for managing metric monitoring and tracking validation + * results. + * + * <p>This class is flag gated by "network_metric_monitor" + */ +public abstract class NetworkMetricMonitor implements AutoCloseable { + private static final String TAG = NetworkMetricMonitor.class.getSimpleName(); + + private static final boolean VDBG = false; // STOPSHIP: if true + + @NonNull private final CloseGuard mCloseGuard = new CloseGuard(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final Network mNetwork; + @NonNull private final NetworkMetricMonitorCallback mCallback; + + private boolean mIsSelectedUnderlyingNetwork; + private boolean mIsStarted; + private boolean mIsValidationFailed; + + protected NetworkMetricMonitor( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + if (!vcnContext.isFlagNetworkMetricMonitorEnabled()) { + // Caller error + logWtf("networkMetricMonitor flag disabled"); + throw new IllegalAccessException("networkMetricMonitor flag disabled"); + } + + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); + mNetwork = Objects.requireNonNull(network, "Missing network"); + mCallback = Objects.requireNonNull(callback, "Missing callback"); + + mIsSelectedUnderlyingNetwork = false; + mIsStarted = false; + mIsValidationFailed = false; + } + + /** Callback to notify caller of the validation result */ + public interface NetworkMetricMonitorCallback { + /** Called when there is a validation result is ready */ + void onValidationResultReceived(); + } + + /** + * Start monitoring + * + * <p>This method might be called on a an already started monitor for updating monitor + * properties (e.g. IpSecTransform, carrier config) + * + * <p>Subclasses MUST call super.start() when overriding this method + */ + protected void start() { + mIsStarted = true; + } + + /** + * Stop monitoring + * + * <p>Subclasses MUST call super.stop() when overriding this method + */ + public void stop() { + mIsValidationFailed = false; + mIsStarted = false; + } + + /** Called by the subclasses when the validation result is ready */ + protected void onValidationResultReceivedInternal(boolean isFailed) { + mIsValidationFailed = isFailed; + mCallback.onValidationResultReceived(); + } + + /** Called when the underlying network changes to selected or unselected */ + protected abstract void onSelectedUnderlyingNetworkChanged(); + + /** + * Mark the network being monitored selected or unselected + * + * <p>Subclasses MUST call super when overriding this method + */ + public void setIsSelectedUnderlyingNetwork(boolean isSelectedUnderlyingNetwork) { + if (mIsSelectedUnderlyingNetwork == isSelectedUnderlyingNetwork) { + return; + } + + mIsSelectedUnderlyingNetwork = isSelectedUnderlyingNetwork; + onSelectedUnderlyingNetworkChanged(); + } + + /** Wrapper that allows injection for testing purposes */ + @VisibleForTesting(visibility = Visibility.PROTECTED) + public static class IpSecTransformWrapper { + @NonNull public final IpSecTransform ipSecTransform; + + public IpSecTransformWrapper(@NonNull IpSecTransform ipSecTransform) { + this.ipSecTransform = ipSecTransform; + } + + /** Poll an IpSecTransformState */ + public void getIpSecTransformState( + @NonNull Executor executor, + @NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) { + ipSecTransform.getIpSecTransformState(executor, callback); + } + + /** Close this instance and release the underlying resources */ + public void close() { + ipSecTransform.close(); + } + + @Override + public int hashCode() { + return Objects.hash(ipSecTransform); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IpSecTransformWrapper)) { + return false; + } + + final IpSecTransformWrapper other = (IpSecTransformWrapper) o; + + return Objects.equals(ipSecTransform, other.ipSecTransform); + } + } + + /** Set the IpSecTransform that applied to the Network being monitored */ + public void setInboundTransform(@NonNull IpSecTransform inTransform) { + setInboundTransformInternal(new IpSecTransformWrapper(inTransform)); + } + + /** + * Set the IpSecTransform that applied to the Network being monitored * + * + * <p>Subclasses MUST call super when overriding this method + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inTransform) { + // Subclasses MUST override it if they care + } + + /** Update the carrierconfig */ + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // Subclasses MUST override it if they care + } + + public boolean isValidationFailed() { + return mIsValidationFailed; + } + + public boolean isSelectedUnderlyingNetwork() { + return mIsSelectedUnderlyingNetwork; + } + + public boolean isStarted() { + return mIsStarted; + } + + @NonNull + public VcnContext getVcnContext() { + return mVcnContext; + } + + // Override methods for AutoCloseable. Subclasses MUST call super when overriding this method + @Override + public void close() { + mCloseGuard.close(); + + stop(); + } + + // Override #finalize() to use closeGuard for flagging that #close() was not called + @SuppressWarnings("Finalize") + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); + } + } + + private String getClassName() { + return this.getClass().getSimpleName(); + } + + protected String getLogPrefix() { + return " [Network " + mNetwork + "] "; + } + + protected void logV(String msg) { + if (VDBG) { + Slog.v(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[VERBOSE ] " + getClassName() + getLogPrefix() + msg); + } + } + + protected void logInfo(String msg) { + Slog.i(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[INFO ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logW(String msg) { + Slog.w(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WARN ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logWtf(String msg) { + Slog.wtf(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WTF ] " + getClassName() + getLogPrefix() + msg); + } + + protected static void logV(String className, String msgWithPrefix) { + if (VDBG) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[VERBOSE ] " + className + msgWithPrefix); + } + } + + protected static void logWtf(String className, String msgWithPrefix) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java index 7f129ea3801c..d32e5cc8ef80 100644 --- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java @@ -47,7 +47,6 @@ import com.android.server.vcn.VcnContext; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; /** @hide */ @@ -86,7 +85,6 @@ class NetworkPriorityClassifier { * <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any * template as the underlying network. */ - @VisibleForTesting(visibility = Visibility.PRIVATE) static final int PRIORITY_INVALID = -1; /** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */ @@ -96,7 +94,7 @@ class NetworkPriorityClassifier { List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED @@ -118,7 +116,7 @@ class NetworkPriorityClassifier { networkRecord, subscriptionGroup, snapshot, - currentlySelected, + isSelected, carrierConfig)) { return priorityIndex; } @@ -140,12 +138,9 @@ class NetworkPriorityClassifier { UnderlyingNetworkRecord networkRecord, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; - final boolean isSelectedUnderlyingNetwork = - currentlySelected != null - && Objects.equals(currentlySelected.network, networkRecord.network); final int meteredMatch = networkPriority.getMetered(); final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED); @@ -159,7 +154,7 @@ class NetworkPriorityClassifier { if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps() || (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinEntryUpstreamBandwidthKbps() - && !isSelectedUnderlyingNetwork)) { + && !isSelected)) { return false; } @@ -167,7 +162,7 @@ class NetworkPriorityClassifier { < networkPriority.getMinExitDownstreamBandwidthKbps() || (caps.getLinkDownstreamBandwidthKbps() < networkPriority.getMinEntryDownstreamBandwidthKbps() - && !isSelectedUnderlyingNetwork)) { + && !isSelected)) { return false; } @@ -191,7 +186,7 @@ class NetworkPriorityClassifier { return checkMatchesWifiPriorityRule( (VcnWifiUnderlyingNetworkTemplate) networkPriority, networkRecord, - currentlySelected, + isSelected, carrierConfig); } @@ -214,7 +209,7 @@ class NetworkPriorityClassifier { public static boolean checkMatchesWifiPriorityRule( VcnWifiUnderlyingNetworkTemplate networkPriority, UnderlyingNetworkRecord networkRecord, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; @@ -223,7 +218,7 @@ class NetworkPriorityClassifier { } // TODO: Move the Network Quality check to the network metric monitor framework. - if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) { + if (!isWifiRssiAcceptable(networkRecord, isSelected, carrierConfig)) { return false; } @@ -237,15 +232,11 @@ class NetworkPriorityClassifier { private static boolean isWifiRssiAcceptable( UnderlyingNetworkRecord networkRecord, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; - final boolean isSelectedNetwork = - currentlySelected != null - && networkRecord.network.equals(currentlySelected.network); - if (isSelectedNetwork - && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) { + if (isSelected && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) { return true; } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index 6afa795e96fa..48df44b7c4ac 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -48,6 +48,7 @@ import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; @@ -83,6 +84,9 @@ public class UnderlyingNetworkController { @NonNull private final TelephonyCallback mActiveDataSubIdListener = new VcnActiveDataSubscriptionIdListener(); + private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords = + new ArrayMap<>(); + @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>(); @Nullable private NetworkCallback mWifiBringupCallback; @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback; @@ -105,7 +109,8 @@ public class UnderlyingNetworkController { this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies()); } - private UnderlyingNetworkController( + @VisibleForTesting(visibility = Visibility.PRIVATE) + UnderlyingNetworkController( @NonNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @@ -196,6 +201,7 @@ public class UnderlyingNetworkController { NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback; List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks); mCellBringupCallbacks.clear(); + mUnderlyingNetworkRecords.clear(); // Register new callbacks. Make-before-break; always register new callbacks before removal // of old callbacks @@ -395,6 +401,18 @@ public class UnderlyingNetworkController { // Update carrier config mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); + // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier + // config to calculate their cached priority classes. For simplicity, the + // UnderlyingNetworkController does not listen for changes in VCN-related carrier config + // keys, and changes are applied at restart of the VcnGatewayConnection + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.reevaluate( + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + } + // Only trigger re-registration if subIds in this group have changed if (oldSnapshot .getAllSubIdsInGroup(mSubscriptionGroup) @@ -418,32 +436,62 @@ public class UnderlyingNetworkController { .unregisterTelephonyCallback(mActiveDataSubIdListener); } + private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() { + TreeSet<UnderlyingNetworkEvaluator> sorted = + new TreeSet<>(UnderlyingNetworkEvaluator.getComparator()); + + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) { + sorted.add(evaluator); + } + } + + return sorted; + } + private void reevaluateNetworks() { if (mIsQuitting || mRouteSelectionCallback == null) { return; // UnderlyingNetworkController has quit. } - TreeSet<UnderlyingNetworkRecord> sorted = - mRouteSelectionCallback.getSortedUnderlyingNetworks(); - UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first(); + TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks(); + + UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first(); + UnderlyingNetworkRecord candidate = + candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord(); if (Objects.equals(mCurrentRecord, candidate)) { return; } String allNetworkPriorities = ""; - for (UnderlyingNetworkRecord record : sorted) { + for (UnderlyingNetworkEvaluator recordEvaluator : sorted) { if (!allNetworkPriorities.isEmpty()) { allNetworkPriorities += ", "; } - allNetworkPriorities += record.network + ": " + record.priorityClass; + allNetworkPriorities += + recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass(); } - logInfo( - "Selected network changed to " - + (candidate == null ? null : candidate.network) - + ", selected from list: " - + allNetworkPriorities); + + if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) { + logInfo( + "Selected network changed to " + + (candidate == null ? null : candidate.network) + + ", selected from list: " + + allNetworkPriorities); + } + mCurrentRecord = candidate; mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); + + // Need to update all evaluators to ensure the previously selected one is unselected + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.setIsSelected( + candidateEvaluator == evaluator, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + } } /** @@ -463,46 +511,26 @@ public class UnderlyingNetworkController { */ @VisibleForTesting class UnderlyingNetworkListener extends NetworkCallback { - private final Map<Network, UnderlyingNetworkRecord.Builder> - mUnderlyingNetworkRecordBuilders = new ArrayMap<>(); - UnderlyingNetworkListener() { super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO); } - private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() { - TreeSet<UnderlyingNetworkRecord> sorted = - new TreeSet<>(UnderlyingNetworkRecord.getComparator()); - - for (UnderlyingNetworkRecord.Builder builder : - mUnderlyingNetworkRecordBuilders.values()) { - if (builder.isValid()) { - final UnderlyingNetworkRecord record = - builder.build( - mVcnContext, - mConnectionConfig.getVcnUnderlyingNetworkPriorities(), - mSubscriptionGroup, - mLastSnapshot, - mCurrentRecord, - mCarrierConfig); - if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) { - sorted.add(record); - } - } - } - - return sorted; - } - @Override public void onAvailable(@NonNull Network network) { - mUnderlyingNetworkRecordBuilders.put( - network, new UnderlyingNetworkRecord.Builder(network)); + mUnderlyingNetworkRecords.put( + network, + mDeps.newUnderlyingNetworkEvaluator( + mVcnContext, + network, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig)); } @Override public void onLost(@NonNull Network network) { - mUnderlyingNetworkRecordBuilders.remove(network); + mUnderlyingNetworkRecords.remove(network); reevaluateNetworks(); } @@ -510,15 +538,20 @@ public class UnderlyingNetworkController { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got capabilities change for unknown key: " + network); return; } - builder.setNetworkCapabilities(networkCapabilities); - if (builder.isValid()) { + evaluator.setNetworkCapabilities( + networkCapabilities, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @@ -526,30 +559,40 @@ public class UnderlyingNetworkController { @Override public void onLinkPropertiesChanged( @NonNull Network network, @NonNull LinkProperties linkProperties) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got link properties change for unknown key: " + network); return; } - builder.setLinkProperties(linkProperties); - if (builder.isValid()) { + evaluator.setLinkProperties( + linkProperties, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @Override public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got blocked status change for unknown key: " + network); return; } - builder.setIsBlocked(isBlocked); - if (builder.isValid()) { + evaluator.setIsBlocked( + isBlocked, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @@ -614,16 +657,8 @@ public class UnderlyingNetworkController { pw.println("Underlying networks:"); pw.increaseIndent(); if (mRouteSelectionCallback != null) { - for (UnderlyingNetworkRecord record : - mRouteSelectionCallback.getSortedUnderlyingNetworks()) { - record.dump( - mVcnContext, - pw, - mConnectionConfig.getVcnUnderlyingNetworkPriorities(), - mSubscriptionGroup, - mLastSnapshot, - mCurrentRecord, - mCarrierConfig); + for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) { + recordEvaluator.dump(pw); } } pw.decreaseIndent(); @@ -653,5 +688,23 @@ public class UnderlyingNetworkController { @Nullable UnderlyingNetworkRecord underlyingNetworkRecord); } - private static class Dependencies {} + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + /** Construct a new UnderlyingNetworkEvaluator */ + public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + return new UnderlyingNetworkEvaluator( + vcnContext, + network, + underlyingNetworkTemplates, + subscriptionGroup, + lastSnapshot, + carrierConfig); + } + } } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java new file mode 100644 index 000000000000..c124a1976ac6 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java @@ -0,0 +1,217 @@ +/* + * 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.server.vcn.routeselection; + +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.vcn.VcnUnderlyingNetworkTemplate; +import android.os.ParcelUuid; + +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.VcnContext; + +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +/** + * UnderlyingNetworkEvaluator evaluates the quality and priority class of a network candidate for + * route selection. + * + * @hide + */ +public class UnderlyingNetworkEvaluator { + private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final UnderlyingNetworkRecord.Builder mNetworkRecordBuilder; + + private boolean mIsSelected; + private int mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID; + + public UnderlyingNetworkEvaluator( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); + + Objects.requireNonNull(underlyingNetworkTemplates, "Missing underlyingNetworkTemplates"); + Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + Objects.requireNonNull(lastSnapshot, "Missing lastSnapshot"); + + mNetworkRecordBuilder = + new UnderlyingNetworkRecord.Builder( + Objects.requireNonNull(network, "Missing network")); + mIsSelected = false; + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + private void updatePriorityClass( + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + if (mNetworkRecordBuilder.isValid()) { + mPriorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + mNetworkRecordBuilder.build(), + underlyingNetworkTemplates, + subscriptionGroup, + lastSnapshot, + mIsSelected, + carrierConfig); + } else { + mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID; + } + } + + public static Comparator<UnderlyingNetworkEvaluator> getComparator() { + return (left, right) -> { + final int leftIndex = left.mPriorityClass; + final int rightIndex = right.mPriorityClass; + + // In the case of networks in the same priority class, prioritize based on other + // criteria (eg. actively selected network, link metrics, etc) + if (leftIndex == rightIndex) { + // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord + // fall into the same priority class. + if (left.mIsSelected) { + return -1; + } + if (right.mIsSelected) { + return 1; + } + } + return Integer.compare(leftIndex, rightIndex); + }; + } + + /** Set the NetworkCapabilities */ + public void setNetworkCapabilities( + @NonNull NetworkCapabilities nc, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setNetworkCapabilities(nc); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set the LinkProperties */ + public void setLinkProperties( + @NonNull LinkProperties lp, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setLinkProperties(lp); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set whether the network is blocked */ + public void setIsBlocked( + boolean isBlocked, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setIsBlocked(isBlocked); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set whether the network is selected as VCN's underlying network */ + public void setIsSelected( + boolean isSelected, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mIsSelected = isSelected; + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** + * Update the last TelephonySubscriptionSnapshot and carrier config to reevaluate the network + */ + public void reevaluate( + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Return whether this network evaluator is valid */ + public boolean isValid() { + return mNetworkRecordBuilder.isValid(); + } + + /** Return the network */ + public Network getNetwork() { + return mNetworkRecordBuilder.getNetwork(); + } + + /** Return the network record */ + public UnderlyingNetworkRecord getNetworkRecord() { + return mNetworkRecordBuilder.build(); + } + + /** Return the priority class for network selection */ + public int getPriorityClass() { + return mPriorityClass; + } + + /** Dump the information of this instance */ + public void dump(IndentingPrintWriter pw) { + pw.println("UnderlyingNetworkEvaluator:"); + pw.increaseIndent(); + + if (mNetworkRecordBuilder.isValid()) { + getNetworkRecord().dump(pw); + } else { + pw.println( + "UnderlyingNetworkRecord incomplete: mNetwork: " + + mNetworkRecordBuilder.getNetwork()); + } + + pw.println("mIsSelected: " + mIsSelected); + pw.println("mPriorityClass: " + mPriorityClass); + + pw.decreaseIndent(); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index aea9f4d2dbae..7ab8e552722a 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -16,24 +16,17 @@ package com.android.server.vcn.routeselection; -import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.vcn.VcnUnderlyingNetworkTemplate; -import android.os.ParcelUuid; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.VcnContext; -import java.util.Comparator; -import java.util.List; import java.util.Objects; /** @@ -46,54 +39,17 @@ public class UnderlyingNetworkRecord { @NonNull public final NetworkCapabilities networkCapabilities; @NonNull public final LinkProperties linkProperties; public final boolean isBlocked; - public final boolean isSelected; - public final int priorityClass; @VisibleForTesting(visibility = Visibility.PRIVATE) public UnderlyingNetworkRecord( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties, - boolean isBlocked, - VcnContext vcnContext, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + boolean isBlocked) { this.network = network; this.networkCapabilities = networkCapabilities; this.linkProperties = linkProperties; this.isBlocked = isBlocked; - - this.isSelected = isSelected(this.network, currentlySelected); - - priorityClass = - NetworkPriorityClassifier.calculatePriorityClass( - vcnContext, - this, - underlyingNetworkTemplates, - subscriptionGroup, - snapshot, - currentlySelected, - carrierConfig); - } - - @VisibleForTesting(visibility = Visibility.PRIVATE) - public UnderlyingNetworkRecord( - @NonNull Network network, - @NonNull NetworkCapabilities networkCapabilities, - @NonNull LinkProperties linkProperties, - boolean isBlocked, - boolean isSelected, - int priorityClass) { - this.network = network; - this.networkCapabilities = networkCapabilities; - this.linkProperties = linkProperties; - this.isBlocked = isBlocked; - this.isSelected = isSelected; - - this.priorityClass = priorityClass; } @Override @@ -113,64 +69,20 @@ public class UnderlyingNetworkRecord { return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); } - /** Returns if two records are equal including their priority classes. */ - public static boolean isEqualIncludingPriorities( - UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) { - if (left != null && right != null) { - return left.equals(right) - && left.isSelected == right.isSelected - && left.priorityClass == right.priorityClass; - } - - return left == right; - } - - static Comparator<UnderlyingNetworkRecord> getComparator() { - return (left, right) -> { - final int leftIndex = left.priorityClass; - final int rightIndex = right.priorityClass; - - // In the case of networks in the same priority class, prioritize based on other - // criteria (eg. actively selected network, link metrics, etc) - if (leftIndex == rightIndex) { - // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord - // fall into the same priority class. - if (left.isSelected) { - return -1; - } - if (right.isSelected) { - return 1; - } - } - return Integer.compare(leftIndex, rightIndex); - }; - } - - private static boolean isSelected( - Network networkToCheck, UnderlyingNetworkRecord currentlySelected) { - if (currentlySelected == null) { - return false; - } - if (currentlySelected.network.equals(networkToCheck)) { - return true; - } - return false; + /** Return whether two records represent the same network */ + public static boolean isSameNetwork( + @Nullable UnderlyingNetworkRecord leftRecord, + @Nullable UnderlyingNetworkRecord rightRecord) { + final Network left = leftRecord == null ? null : leftRecord.network; + final Network right = rightRecord == null ? null : rightRecord.network; + return Objects.equals(left, right); } /** Dumps the state of this record for logging and debugging purposes. */ - void dump( - VcnContext vcnContext, - IndentingPrintWriter pw, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + void dump(IndentingPrintWriter pw) { pw.println("UnderlyingNetworkRecord:"); pw.increaseIndent(); - pw.println("priorityClass: " + priorityClass); - pw.println("isSelected: " + isSelected); pw.println("mNetwork: " + network); pw.println("mNetworkCapabilities: " + networkCapabilities); pw.println("mLinkProperties: " + linkProperties); @@ -218,29 +130,14 @@ public class UnderlyingNetworkRecord { return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet; } - UnderlyingNetworkRecord build( - VcnContext vcnContext, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + UnderlyingNetworkRecord build() { if (!isValid()) { throw new IllegalArgumentException( "Called build before UnderlyingNetworkRecord was valid"); } return new UnderlyingNetworkRecord( - mNetwork, - mNetworkCapabilities, - mLinkProperties, - mIsBlocked, - vcnContext, - underlyingNetworkTemplates, - subscriptionGroup, - snapshot, - currentlySelected, - carrierConfig); + mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); } } } diff --git a/services/core/java/com/android/server/wearable/OWNERS b/services/core/java/com/android/server/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/services/core/java/com/android/server/wearable/OWNERS +++ b/services/core/java/com/android/server/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java index 05da9dfe7921..e5c743cc69e4 100644 --- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java +++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; + import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -180,16 +181,8 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, if (snapshot == null) { return null; } - final HardwareBuffer buffer = snapshot.getHardwareBuffer(); - if (buffer.getWidth() == 0 || buffer.getHeight() == 0) { - buffer.close(); - Slog.e(TAG, "Invalid snapshot dimensions " + buffer.getWidth() + "x" - + buffer.getHeight()); - return null; - } else { - mCache.putSnapshot(source, snapshot); - return snapshot; - } + mCache.putSnapshot(source, snapshot); + return snapshot; } @VisibleForTesting @@ -210,6 +203,11 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, @Nullable TaskSnapshot snapshot(TYPE source) { + return snapshot(source, mHighResSnapshotScale); + } + + @Nullable + TaskSnapshot snapshot(TYPE source, float scale) { TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); final Rect crop = prepareTaskSnapshot(source, builder); if (crop == null) { @@ -218,7 +216,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot"); final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshot(source, - mHighResSnapshotScale, crop, builder); + scale, crop, builder); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); if (screenshotBuffer == null) { // Failed to acquire image. Has been logged. @@ -227,7 +225,19 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, builder.setCaptureTime(SystemClock.elapsedRealtimeNanos()); builder.setSnapshot(screenshotBuffer.getHardwareBuffer()); builder.setColorSpace(screenshotBuffer.getColorSpace()); - return builder.build(); + final TaskSnapshot snapshot = builder.build(); + return validateSnapshot(snapshot); + } + + private static TaskSnapshot validateSnapshot(@NonNull TaskSnapshot snapshot) { + final HardwareBuffer buffer = snapshot.getHardwareBuffer(); + if (buffer.getWidth() == 0 || buffer.getHeight() == 0) { + buffer.close(); + Slog.e(TAG, "Invalid snapshot dimensions " + buffer.getWidth() + "x" + + buffer.getHeight()); + return null; + } + return snapshot; } @Nullable @@ -432,7 +442,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, InsetUtils.addInsets(contentInsets, letterboxInsets); // Note, the app theme snapshot is never translucent because we enforce a non-translucent // color above - return new TaskSnapshot( + final TaskSnapshot taskSnapshot = new TaskSnapshot( System.currentTimeMillis() /* id */, SystemClock.elapsedRealtimeNanos() /* captureTime */, topActivity.mActivityComponent, hwBitmap.getHardwareBuffer(), @@ -441,6 +451,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, contentInsets, letterboxInsets, false /* isLowResolution */, false /* isRealSnapshot */, source.getWindowingMode(), getAppearance(source), false /* isTranslucent */, false /* hasImeSurface */); + return validateSnapshot(taskSnapshot); } static Rect getSystemBarInsets(Rect frame, InsetsState state) { diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 4b55bec928c7..676203bc746a 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1166,11 +1166,12 @@ class ActivityClientController extends IActivityClientController.Stub { transition.abort(); return; } - transition.collect(topFocusedRootTask); - executeMultiWindowFullscreenRequest(fullscreenRequest, topFocusedRootTask); - r.mTransitionController.requestStartTransition(transition, topFocusedRootTask, + final Task requestingTask = r.getTask(); + transition.collect(requestingTask); + executeMultiWindowFullscreenRequest(fullscreenRequest, requestingTask); + r.mTransitionController.requestStartTransition(transition, requestingTask, null /* remoteTransition */, null /* displayChange */); - transition.setReady(topFocusedRootTask, true); + transition.setReady(requestingTask, true); } private static void reportMultiwindowFullscreenRequestValidatingResult(IRemoteCallback callback, diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index 7af494c296de..a692167bbbf9 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -25,6 +25,7 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.Trace; import android.util.ArraySet; +import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.window.TaskSnapshot; @@ -36,6 +37,7 @@ import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; import com.android.window.flags.Flags; import java.io.File; +import java.io.PrintWriter; import java.util.ArrayList; /** @@ -136,11 +138,26 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord false /* enableLowResSnapshots */, 0 /* lowResScaleFactor */, use16BitFormat); } - /** Retrieves a snapshot for an activity from cache. */ + /** + * Retrieves a snapshot for a set of activities from cache. + * This will only return the snapshot IFF input activities exist entirely in the snapshot. + * Sample: If the snapshot was captured with activity A and B, here will return null if the + * input activity is only [A] or [B], it must be [A, B] + */ @Nullable - TaskSnapshot getSnapshot(ActivityRecord ar) { - final int code = getSystemHashCode(ar); - return mCache.getSnapshot(code); + TaskSnapshot getSnapshot(@NonNull ActivityRecord[] activities) { + if (activities.length == 0) { + return null; + } + final UserSavedFile tmpUsf = findSavedFile(activities[0]); + if (tmpUsf == null || tmpUsf.mActivityIds.size() != activities.length) { + return null; + } + int fileId = 0; + for (int i = activities.length - 1; i >= 0; --i) { + fileId ^= getSystemHashCode(activities[i]); + } + return tmpUsf.mFileId == fileId ? mCache.getSnapshot(tmpUsf.mActivityIds.get(0)) : null; } private void cleanUpUserFiles(int userId) { @@ -229,33 +246,16 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord + " load " + mPendingLoadActivity); } // load snapshot to cache - for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) { - final ActivityRecord ar = mPendingLoadActivity.valueAt(i); - final int code = getSystemHashCode(ar); - final int userId = ar.mUserId; - if (mCache.getSnapshot(code) != null) { - // already in cache, skip - continue; - } - if (containsFile(code, userId)) { - synchronized (mSnapshotPersistQueue.getLock()) { - mSnapshotPersistQueue.insertQueueAtFirstLocked( - new LoadActivitySnapshotItem(ar, code, userId, mPersistInfoProvider)); - } - } - } + loadActivitySnapshot(); // clear mTmpRemoveActivity from cache for (int i = mPendingRemoveActivity.size() - 1; i >= 0; i--) { final ActivityRecord ar = mPendingRemoveActivity.valueAt(i); - final int code = getSystemHashCode(ar); - mCache.onIdRemoved(code); + removeCachedFiles(ar); } // clear snapshot on cache and delete files for (int i = mPendingDeleteActivity.size() - 1; i >= 0; i--) { final ActivityRecord ar = mPendingDeleteActivity.valueAt(i); - final int code = getSystemHashCode(ar); - mCache.onIdRemoved(code); - removeIfUserSavedFileExist(code, ar.mUserId); + removeIfUserSavedFileExist(ar); } // don't keep any reference resetTmpFields(); @@ -264,28 +264,38 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord class LoadActivitySnapshotItem extends SnapshotPersistQueue.WriteQueueItem { private final int mCode; private final int mUserId; - private final ActivityRecord mActivityRecord; + private final ActivityRecord[] mActivities; - LoadActivitySnapshotItem(@NonNull ActivityRecord ar, int code, int userId, + LoadActivitySnapshotItem(@NonNull ActivityRecord[] activities, int code, int userId, @NonNull PersistInfoProvider persistInfoProvider) { super(persistInfoProvider); - mActivityRecord = ar; + mActivities = activities; mCode = code; mUserId = userId; } @Override void write() { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, - "load_activity_snapshot"); - final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode, - mUserId, false /* loadLowResolutionBitmap */); - synchronized (mService.getWindowManagerLock()) { - if (snapshot != null && !mActivityRecord.finishing) { - mCache.putSnapshot(mActivityRecord, snapshot); + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "load_activity_snapshot"); + final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode, + mUserId, false /* loadLowResolutionBitmap */); + if (snapshot == null) { + return; + } + synchronized (mService.getWindowManagerLock()) { + // Verify the snapshot is still needed, and the activity is not finishing + if (!hasRecord(mActivities[0])) { + return; + } + for (ActivityRecord ar : mActivities) { + mCache.putSnapshot(ar, snapshot); + } } + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @Override @@ -297,18 +307,81 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } } - void recordSnapshot(ActivityRecord activity) { - if (shouldDisableSnapshots()) { + void loadActivitySnapshot() { + if (mPendingLoadActivity.isEmpty()) { + return; + } + // Only load if saved file exists. + final ArraySet<UserSavedFile> loadingFiles = new ArraySet<>(); + for (int i = mPendingLoadActivity.size() - 1; i >= 0; i--) { + final ActivityRecord ar = mPendingLoadActivity.valueAt(i); + final UserSavedFile usf = findSavedFile(ar); + if (usf != null) { + loadingFiles.add(usf); + } + } + // Filter out the activity if the snapshot was removed. + for (int i = loadingFiles.size() - 1; i >= 0; i--) { + final UserSavedFile usf = loadingFiles.valueAt(i); + final ActivityRecord[] activities = usf.filterExistActivities(mPendingLoadActivity); + if (activities == null) { + continue; + } + if (getSnapshot(activities) != null) { + // Found the cache in memory, so skip loading from file. + continue; + } + loadSnapshotInner(activities, usf); + } + } + + @VisibleForTesting + void loadSnapshotInner(ActivityRecord[] activities, UserSavedFile usf) { + synchronized (mSnapshotPersistQueue.getLock()) { + mSnapshotPersistQueue.insertQueueAtFirstLocked(new LoadActivitySnapshotItem( + activities, usf.mFileId, usf.mUserId, mPersistInfoProvider)); + } + } + + /** + * Record one or multiple activities within a snapshot where those activities must belong to + * the same task. + * @param activity If the request activity is more than one, try to record those activities + * as a single snapshot, so those activities should belong to the same task. + */ + void recordSnapshot(@NonNull ArrayList<ActivityRecord> activity) { + if (shouldDisableSnapshots() || activity.isEmpty()) { return; } if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#recordSnapshot " + activity); } - final TaskSnapshot snapshot = recordSnapshotInner(activity); - if (snapshot != null) { - final int code = getSystemHashCode(activity); - addUserSavedFile(code, activity.mUserId, snapshot); + final int size = activity.size(); + final int[] mixedCode = new int[size]; + if (size == 1) { + final ActivityRecord singleActivity = activity.get(0); + final TaskSnapshot snapshot = recordSnapshotInner(singleActivity); + if (snapshot != null) { + mixedCode[0] = getSystemHashCode(singleActivity); + addUserSavedFile(singleActivity.mUserId, snapshot, mixedCode); + } + return; } + + final Task mainTask = activity.get(0).getTask(); + // Snapshot by task controller with activity's scale. + final TaskSnapshot snapshot = mService.mTaskSnapshotController + .snapshot(mainTask, mHighResSnapshotScale); + if (snapshot == null) { + return; + } + + for (int i = 0; i < activity.size(); ++i) { + final ActivityRecord next = activity.get(i); + mCache.putSnapshot(next, snapshot); + mixedCode[i] = getSystemHashCode(next); + } + addUserSavedFile(mainTask.mUserId, snapshot, mixedCode); } /** @@ -331,7 +404,8 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } } - private static int getSystemHashCode(ActivityRecord activity) { + @VisibleForTesting + static int getSystemHashCode(ActivityRecord activity) { return System.identityHashCode(activity); } @@ -362,7 +436,13 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (ar.isVisibleRequested()) { mPendingDeleteActivity.add(ar); // load next one if exists. - addBelowActivityIfExist(ar, mPendingLoadActivity, true, "load-snapshot"); + // Note if this transition is happen between two TaskFragment, the next N - 1 activity + // may not participant in this transition. + // Sample: + // [TF1] close + // [TF2] open + // Bottom Activity <- Able to load this even it didn't participant the transition. + addBelowActivityIfExist(ar, mPendingLoadActivity, false, "load-snapshot"); } else { // remove the snapshot for the one below close addBelowActivityIfExist(ar, mPendingRemoveActivity, true, "remove-snapshot"); @@ -478,10 +558,8 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } private void adjustSavedFileOrder(Task nextTopTask) { - final int userId = nextTopTask.mUserId; nextTopTask.forAllActivities(ar -> { - final int code = getSystemHashCode(ar); - final UserSavedFile usf = getUserFiles(userId).get(code); + final UserSavedFile usf = findSavedFile(ar); if (usf != null) { mSavedFilesInOrder.remove(usf); mSavedFilesInOrder.add(usf); @@ -494,9 +572,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (shouldDisableSnapshots()) { return; } - super.onAppRemoved(activity); - final int code = getSystemHashCode(activity); - removeIfUserSavedFileExist(code, activity.mUserId); + removeIfUserSavedFileExist(activity); if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#onAppRemoved delete snapshot " + activity); } @@ -507,9 +583,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord if (shouldDisableSnapshots()) { return; } - super.onAppDied(activity); - final int code = getSystemHashCode(activity); - removeIfUserSavedFileExist(code, activity.mUserId); + removeIfUserSavedFileExist(activity); if (DEBUG) { Slog.d(TAG, "ActivitySnapshotController#onAppDied delete snapshot " + activity); } @@ -558,55 +632,92 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord return mUserSavedFiles.get(userId); } - private void removeIfUserSavedFileExist(int code, int userId) { - final UserSavedFile usf = getUserFiles(userId).get(code); + UserSavedFile findSavedFile(@NonNull ActivityRecord ar) { + final int code = getSystemHashCode(ar); + return findSavedFile(ar.mUserId, code); + } + + UserSavedFile findSavedFile(int userId, int code) { + final SparseArray<UserSavedFile> usfs = getUserFiles(userId); + return usfs.get(code); + } + + private void removeCachedFiles(ActivityRecord ar) { + final UserSavedFile usf = findSavedFile(ar); + if (usf != null) { + for (int i = usf.mActivityIds.size() - 1; i >= 0; --i) { + final int activityId = usf.mActivityIds.get(i); + mCache.onIdRemoved(activityId); + } + } + } + + private void removeIfUserSavedFileExist(ActivityRecord ar) { + final UserSavedFile usf = findSavedFile(ar); if (usf != null) { - mUserSavedFiles.get(userId).remove(code); + final SparseArray<UserSavedFile> usfs = getUserFiles(ar.mUserId); + for (int i = usf.mActivityIds.size() - 1; i >= 0; --i) { + final int activityId = usf.mActivityIds.get(i); + usf.remove(activityId); + mCache.onIdRemoved(activityId); + usfs.remove(activityId); + } mSavedFilesInOrder.remove(usf); - mPersister.removeSnapshot(code, userId); + mPersister.removeSnapshot(usf.mFileId, ar.mUserId); } } - private boolean containsFile(int code, int userId) { - return getUserFiles(userId).get(code) != null; + @VisibleForTesting + boolean hasRecord(@NonNull ActivityRecord ar) { + return findSavedFile(ar) != null; } - private void addUserSavedFile(int code, int userId, TaskSnapshot snapshot) { - final SparseArray<UserSavedFile> savedFiles = getUserFiles(userId); - final UserSavedFile savedFile = savedFiles.get(code); - if (savedFile == null) { - final UserSavedFile usf = new UserSavedFile(code, userId); - savedFiles.put(code, usf); - mSavedFilesInOrder.add(usf); - mPersister.persistSnapshot(code, userId, snapshot); + @VisibleForTesting + void addUserSavedFile(int userId, TaskSnapshot snapshot, @NonNull int[] code) { + final UserSavedFile savedFile = findSavedFile(userId, code[0]); + if (savedFile != null) { + Slog.w(TAG, "Duplicate request for recording activity snapshot " + savedFile); + return; + } + int fileId = 0; + for (int i = code.length - 1; i >= 0; --i) { + fileId ^= code[i]; + } + final UserSavedFile usf = new UserSavedFile(fileId, userId); + SparseArray<UserSavedFile> usfs = getUserFiles(userId); + for (int i = code.length - 1; i >= 0; --i) { + usfs.put(code[i], usf); + } + usf.mActivityIds.addAll(code); + mSavedFilesInOrder.add(usf); + mPersister.persistSnapshot(fileId, userId, snapshot); - if (mSavedFilesInOrder.size() > MAX_PERSIST_SNAPSHOT_COUNT * 2) { - purgeSavedFile(); - } + if (mSavedFilesInOrder.size() > MAX_PERSIST_SNAPSHOT_COUNT * 2) { + purgeSavedFile(); } } private void purgeSavedFile() { final int savedFileCount = mSavedFilesInOrder.size(); final int removeCount = savedFileCount - MAX_PERSIST_SNAPSHOT_COUNT; - final ArrayList<UserSavedFile> usfs = new ArrayList<>(); - if (removeCount > 0) { - final int removeTillIndex = savedFileCount - removeCount; - for (int i = savedFileCount - 1; i > removeTillIndex; --i) { - final UserSavedFile usf = mSavedFilesInOrder.remove(i); - if (usf != null) { - final SparseArray<UserSavedFile> records = getUserFiles(usf.mUserId); - records.remove(usf.mFileId); - usfs.add(usf); - } - } + if (removeCount < 1) { + return; } - if (usfs.size() > 0) { - removeSnapshotFiles(usfs); + + final ArrayList<UserSavedFile> removeTargets = new ArrayList<>(); + for (int i = removeCount - 1; i >= 0; --i) { + final UserSavedFile usf = mSavedFilesInOrder.remove(i); + final SparseArray<UserSavedFile> files = mUserSavedFiles.get(usf.mUserId); + for (int j = usf.mActivityIds.size() - 1; j >= 0; --j) { + mCache.removeRunningEntry(usf.mActivityIds.get(j)); + files.remove(usf.mActivityIds.get(j)); + } + removeTargets.add(usf); } + removeSnapshotFiles(removeTargets); } - private void removeSnapshotFiles(ArrayList<UserSavedFile> files) { + private void removeSnapshotFiles(@NonNull ArrayList<UserSavedFile> files) { synchronized (mSnapshotPersistQueue.getLock()) { mSnapshotPersistQueue.sendToQueueLocked( new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) { @@ -624,12 +735,85 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } } + @Override + void dump(PrintWriter pw, String prefix) { + super.dump(pw, prefix); + final String doublePrefix = prefix + " "; + final String triplePrefix = doublePrefix + " "; + for (int i = mUserSavedFiles.size() - 1; i >= 0; --i) { + final SparseArray<UserSavedFile> usfs = mUserSavedFiles.valueAt(i); + pw.println(doublePrefix + "UserSavedFile userId=" + mUserSavedFiles.keyAt(i)); + final ArraySet<UserSavedFile> sets = new ArraySet<>(); + for (int j = usfs.size() - 1; j >= 0; --j) { + sets.add(usfs.valueAt(j)); + } + for (int j = sets.size() - 1; j >= 0; --j) { + pw.println(triplePrefix + "SavedFile=" + sets.valueAt(j)); + } + } + } + static class UserSavedFile { - int mFileId; - int mUserId; + // The unique id as filename. + final int mFileId; + final int mUserId; + + /** + * The Id of all activities which are includes in the snapshot. + */ + final IntArray mActivityIds = new IntArray(); + UserSavedFile(int fileId, int userId) { mFileId = fileId; mUserId = userId; } + + boolean contains(int code) { + return mActivityIds.contains(code); + } + + void remove(int code) { + final int index = mActivityIds.indexOf(code); + if (index >= 0) { + mActivityIds.remove(index); + } + } + + ActivityRecord[] filterExistActivities( + @NonNull ArraySet<ActivityRecord> pendingLoadActivity) { + ArrayList<ActivityRecord> matchedActivities = null; + for (int i = pendingLoadActivity.size() - 1; i >= 0; --i) { + final ActivityRecord ar = pendingLoadActivity.valueAt(i); + if (contains(getSystemHashCode(ar))) { + if (matchedActivities == null) { + matchedActivities = new ArrayList<>(); + } + matchedActivities.add(ar); + } + } + if (matchedActivities == null || matchedActivities.size() != mActivityIds.size()) { + return null; + } + return matchedActivities.toArray(new ActivityRecord[0]); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("UserSavedFile{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" fileId="); + sb.append(Integer.toHexString(mFileId)); + sb.append(", activityIds=["); + for (int i = mActivityIds.size() - 1; i >= 0; --i) { + sb.append(Integer.toHexString(mActivityIds.get(i))); + if (i > 0) { + sb.append(','); + } + } + sb.append("]"); + sb.append("}"); + return sb.toString(); + } } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 22d17b596c4c..8aaf76a165ab 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1311,7 +1311,7 @@ class BackNavigationController { Rect insets; if (mainWindow != null) { insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( - mBounds, WindowInsets.Type.systemBars(), + mBounds, WindowInsets.Type.tappableElement(), false /* ignoreVisibility */).toRect(); InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); } else { @@ -1327,7 +1327,8 @@ class BackNavigationController { return mAnimationTarget; } - void createStartingSurface(@NonNull WindowContainer closeWindow) { + void createStartingSurface(@NonNull WindowContainer closeWindow, + @NonNull ActivityRecord[] visibleOpenActivities) { if (!mIsOpen) { return; } @@ -1346,7 +1347,7 @@ class BackNavigationController { if (mainActivity == null) { return; } - final TaskSnapshot snapshot = getSnapshot(mTarget); + final TaskSnapshot snapshot = getSnapshot(mTarget, visibleOpenActivities); mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController .addWindowlessStartingSurface(openTask, mainActivity, // Choose configuration from closeWindow, because the configuration @@ -1489,7 +1490,8 @@ class BackNavigationController { // Try to draw two snapshot within a WindowlessStartingWindow, or find // another key for StartingWindowRecordManager. && openAnimationAdaptor.length == 1) { - openAnimationAdaptor[0].createStartingSurface(closeWindow); + openAnimationAdaptor[0].createStartingSurface(closeWindow, + visibleOpenActivities); } else { for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { setLaunchBehind(visibleOpenActivities[i]); @@ -1594,7 +1596,9 @@ class BackNavigationController { // skip commitVisibility call in setVisibility cause the activity won't visible here. // Call it again to make sure the activity could be visible while handling the pending // animation. - activity.commitVisibility(true, true); + // Do not performLayout during prepare animation, because it could cause focus window + // change. Let that happen after the BackNavigationInfo has returned to shell. + activity.commitVisibility(true, false /* performLayout */); activity.mTransitionController.mSnapshotController .mActivitySnapshotController.addOnBackPressedActivity(activity); } @@ -1671,7 +1675,8 @@ class BackNavigationController { mPendingAnimationBuilder = null; } - static TaskSnapshot getSnapshot(@NonNull WindowContainer w) { + static TaskSnapshot getSnapshot(@NonNull WindowContainer w, + ActivityRecord[] visibleOpenActivities) { if (w.asTask() != null) { final Task task = w.asTask(); return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( @@ -1681,7 +1686,8 @@ class BackNavigationController { if (w.asActivityRecord() != null) { final ActivityRecord ar = w.asActivityRecord(); - return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar); + return ar.mWmService.mSnapshotController.mActivitySnapshotController + .getSnapshot(visibleOpenActivities); } return null; } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index fc3a33883de6..9c9cf04de3cc 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -120,38 +120,56 @@ public class BackgroundActivityStartController { static final int BAL_BLOCK = 0; - static final int BAL_ALLOW_DEFAULT = 1; + static final int BAL_ALLOW_DEFAULT = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_DEFAULT; // Following codes are in order of precedence /** Important UIDs which should be always allowed to launch activities */ - static final int BAL_ALLOW_ALLOWLISTED_UID = 2; + static final int BAL_ALLOW_ALLOWLISTED_UID = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_UID; /** Apps that fulfill a certain role that can can always launch new tasks */ - static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = 3; + static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_COMPONENT; - /** Apps which currently have a visible window or are bound by a service with a visible - * window */ - static final int BAL_ALLOW_VISIBLE_WINDOW = 4; + /** + * Apps which currently have a visible window or are bound by a service with a visible + * window + */ + static final int BAL_ALLOW_VISIBLE_WINDOW = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_VISIBLE_WINDOW; /** Allowed due to the PendingIntent sender */ - static final int BAL_ALLOW_PENDING_INTENT = 5; + static final int BAL_ALLOW_PENDING_INTENT = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_PENDING_INTENT; - /** App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges - * granted to it */ - static final int BAL_ALLOW_PERMISSION = 6; + /** + * App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges + * granted to it + */ + static final int BAL_ALLOW_PERMISSION = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_BAL_PERMISSION; /** Process has SYSTEM_ALERT_WINDOW permission granted to it */ - static final int BAL_ALLOW_SAW_PERMISSION = 7; + static final int BAL_ALLOW_SAW_PERMISSION = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SAW_PERMISSION; /** App is in grace period after an activity was started or finished */ - static final int BAL_ALLOW_GRACE_PERIOD = 8; + static final int BAL_ALLOW_GRACE_PERIOD = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_GRACE_PERIOD; /** App is in a foreground task or bound to a foreground service (but not itself visible) */ - static final int BAL_ALLOW_FOREGROUND = 9; + static final int BAL_ALLOW_FOREGROUND = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_FOREGROUND; /** Process belongs to a SDK sandbox */ - static final int BAL_ALLOW_SDK_SANDBOX = 10; + static final int BAL_ALLOW_SDK_SANDBOX = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SDK_SANDBOX; + + /** Process belongs to a SDK sandbox */ + static final int BAL_ALLOW_NON_APP_VISIBLE_WINDOW = + FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_NON_APP_VISIBLE_WINDOW; static String balCodeToString(@BalCode int balCode) { return switch (balCode) { @@ -160,6 +178,7 @@ public class BackgroundActivityStartController { case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT"; case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND"; case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD"; + case BAL_ALLOW_NON_APP_VISIBLE_WINDOW -> "BAL_ALLOW_NON_APP_VISIBLE_WINDOW"; case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT"; case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION"; case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION"; @@ -788,7 +807,7 @@ public class BackgroundActivityStartController { /*background*/ false, "callingUid has visible window"); } if (mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) { - return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, + return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW, /*background*/ false, "callingUid has non-app visible window"); } @@ -884,7 +903,7 @@ public class BackgroundActivityStartController { /*background*/ false, "realCallingUid has visible window"); } if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) { - return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, + return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW, /*background*/ false, "realCallingUid has non-app visible window"); } } else { @@ -989,7 +1008,8 @@ public class BackgroundActivityStartController { || balCode == BAL_ALLOW_PERMISSION || balCode == BAL_ALLOW_PENDING_INTENT || balCode == BAL_ALLOW_SAW_PERMISSION - || balCode == BAL_ALLOW_VISIBLE_WINDOW) { + || balCode == BAL_ALLOW_VISIBLE_WINDOW + || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW) { return true; } } @@ -1501,7 +1521,8 @@ public class BackgroundActivityStartController { Intent intent = state.mIntent; if (code == BAL_ALLOW_PENDING_INTENT - && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) { + && (callingUid < Process.FIRST_APPLICATION_UID + || realCallingUid < Process.FIRST_APPLICATION_UID)) { String activityName = intent != null ? requireNonNull(intent.getComponent()).flattenToShortString() : ""; writeBalAllowedLog(activityName, BAL_ALLOW_PENDING_INTENT, diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index ed54ea8229fe..f10a733040ed 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -75,6 +75,7 @@ import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; +import android.view.View.FocusDirection; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager; @@ -1000,6 +1001,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } return didTransfer; } + + @Override + public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) { + final long identity = Binder.clearCallingIdentity(); + try { + return mService.moveFocusToAdjacentWindow(this, fromWindow, direction); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) { diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index b6f040a6cb56..3014f979aa70 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -160,9 +160,7 @@ class SnapshotController { if (!allOpensOptInOnBackInvoked() || mCloseActivities.isEmpty()) { return; } - for (int i = mCloseActivities.size() - 1; i >= 0; --i) { - controller.recordSnapshot(mCloseActivities.get(i)); - } + controller.recordSnapshot(mCloseActivities); } } } diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java index bffdf54e17ce..e4379b5343f3 100644 --- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -274,7 +274,9 @@ class SnapshotPersistQueue { @Override void write() { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem"); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem#" + mId); + } if (!mPersistInfoProvider.createDirectory(mUserId)) { Slog.e(TAG, "Unable to create snapshot directory for user dir=" + mPersistInfoProvider.getDirectory(mUserId)); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c63cc4373472..95448352736f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -287,6 +287,7 @@ import android.view.SurfaceControlViewHost; import android.view.SurfaceSession; import android.view.TaskTransitionSpec; import android.view.View; +import android.view.View.FocusDirection; import android.view.ViewDebug; import android.view.WindowContentFrameStats; import android.view.WindowInsets; @@ -9104,6 +9105,66 @@ public class WindowManagerService extends IWindowManager.Stub win.mClient); } + boolean moveFocusToAdjacentWindow(Session session, IWindow fromWindow, + @FocusDirection int direction) { + synchronized (mGlobalLock) { + final WindowState fromWin = windowForClientLocked(session, fromWindow, false); + if (fromWin == null || !fromWin.isFocused()) { + return false; + } + final TaskFragment fromFragment = fromWin.getTaskFragment(); + if (fromFragment == null) { + return false; + } + final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment(); + if (adjacentFragment == null || adjacentFragment.asTask() != null) { + // Don't move the focus to another task. + return false; + } + final Rect fromBounds = fromFragment.getBounds(); + final Rect adjacentBounds = adjacentFragment.getBounds(); + switch (direction) { + case View.FOCUS_LEFT: + if (adjacentBounds.left >= fromBounds.left) { + return false; + } + break; + case View.FOCUS_UP: + if (adjacentBounds.top >= fromBounds.top) { + return false; + } + break; + case View.FOCUS_RIGHT: + if (adjacentBounds.right <= fromBounds.right) { + return false; + } + break; + case View.FOCUS_DOWN: + if (adjacentBounds.bottom <= fromBounds.bottom) { + return false; + } + break; + case View.FOCUS_BACKWARD: + case View.FOCUS_FORWARD: + // These are not absolute directions. Skip checking the bounds. + break; + default: + return false; + } + final ActivityRecord topRunningActivity = adjacentFragment.topRunningActivity( + true /* focusableOnly */); + if (topRunningActivity == null) { + return false; + } + moveDisplayToTopInternal(topRunningActivity.getDisplayId()); + handleTaskFocusChange(topRunningActivity.getTask(), topRunningActivity); + if (fromWin.isFocused()) { + return false; + } + } + return true; + } + /** Return whether layer tracing is enabled */ public boolean isLayerTracing() { if (!checkCallingPermission( diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index afb0b20650f8..414339d3f349 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -299,7 +299,7 @@ public: void setSystemUiLightsOut(bool lightsOut); void setPointerDisplayId(int32_t displayId); void setPointerSpeed(int32_t speed); - void setPointerAcceleration(float acceleration); + void setMousePointerAccelerationEnabled(bool enabled); void setTouchpadPointerSpeed(int32_t speed); void setTouchpadNaturalScrollingEnabled(bool enabled); void setTouchpadTapToClickEnabled(bool enabled); @@ -411,8 +411,8 @@ private: // Pointer speed. int32_t pointerSpeed{0}; - // Pointer acceleration. - float pointerAcceleration{android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION}; + // True if pointer acceleration is enabled for mice. + bool mousePointerAccelerationEnabled{true}; // True if pointer gestures are enabled. bool pointerGesturesEnabled{true}; @@ -502,7 +502,8 @@ void NativeInputManager::dump(std::string& dump) { dump += StringPrintf(INDENT "System UI Lights Out: %s\n", toString(mLocked.systemUiLightsOut)); dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed); - dump += StringPrintf(INDENT "Pointer Acceleration: %0.3f\n", mLocked.pointerAcceleration); + dump += StringPrintf(INDENT "Mouse Pointer Acceleration: %s\n", + mLocked.mousePointerAccelerationEnabled ? "Enabled" : "Disabled"); dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n", toString(mLocked.pointerGesturesEnabled)); dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches)); @@ -686,7 +687,10 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed * POINTER_SPEED_EXPONENT); - outConfig->pointerVelocityControlParameters.acceleration = mLocked.pointerAcceleration; + outConfig->pointerVelocityControlParameters.acceleration = + mLocked.mousePointerAccelerationEnabled + ? android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION + : 1; outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled; outConfig->showTouches = mLocked.showTouches; @@ -1207,16 +1211,16 @@ void NativeInputManager::setPointerSpeed(int32_t speed) { InputReaderConfiguration::Change::POINTER_SPEED); } -void NativeInputManager::setPointerAcceleration(float acceleration) { +void NativeInputManager::setMousePointerAccelerationEnabled(bool enabled) { { // acquire lock std::scoped_lock _l(mLock); - if (mLocked.pointerAcceleration == acceleration) { + if (mLocked.mousePointerAccelerationEnabled == enabled) { return; } - ALOGI("Setting pointer acceleration to %0.3f", acceleration); - mLocked.pointerAcceleration = acceleration; + ALOGI("Setting mouse pointer acceleration to %s", toString(enabled)); + mLocked.mousePointerAccelerationEnabled = enabled; } // release lock mInputManager->getReader().requestRefreshConfiguration( @@ -2174,10 +2178,11 @@ static void nativeSetPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed im->setPointerSpeed(speed); } -static void nativeSetPointerAcceleration(JNIEnv* env, jobject nativeImplObj, jfloat acceleration) { +static void nativeSetMousePointerAccelerationEnabled(JNIEnv* env, jobject nativeImplObj, + jboolean enabled) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - im->setPointerAcceleration(acceleration); + im->setMousePointerAccelerationEnabled(enabled); } static void nativeSetTouchpadPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) { @@ -2812,7 +2817,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*)nativeTransferTouchFocus}, {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouch}, {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed}, - {"setPointerAcceleration", "(F)V", (void*)nativeSetPointerAcceleration}, + {"setMousePointerAccelerationEnabled", "(Z)V", + (void*)nativeSetMousePointerAccelerationEnabled}, {"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed}, {"setTouchpadNaturalScrollingEnabled", "(Z)V", (void*)nativeSetTouchpadNaturalScrollingEnabled}, diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml index 070bd4b1c5a9..2ccd1e4c00c7 100644 --- a/services/core/lint-baseline.xml +++ b/services/core/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 7.2.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -145,4 +145,4 @@ line="7158"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/lint-baseline.xml b/services/lint-baseline.xml index 8489c17dd878..a311d07e52fb 100644 --- a/services/lint-baseline.xml +++ b/services/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -56,4 +56,4 @@ column="13"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/print/lint-baseline.xml b/services/print/lint-baseline.xml index 1bf031a9e289..11c0cc8ea93c 100644 --- a/services/print/lint-baseline.xml +++ b/services/print/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -12,4 +12,4 @@ column="13"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java deleted file mode 100644 index 4cc68cf11e69..000000000000 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java +++ /dev/null @@ -1,1971 +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.android.server.display; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.res.Resources; -import android.hardware.Sensor; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.hardware.display.BrightnessInfo; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; -import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.PowerManager; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.os.test.TestLooper; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.provider.Settings; -import android.testing.TestableContext; -import android.util.FloatProperty; -import android.util.SparseArray; -import android.view.Display; -import android.view.DisplayInfo; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.modules.utils.testing.ExtendedMockitoRule; -import com.android.server.LocalServices; -import com.android.server.am.BatteryStatsService; -import com.android.server.display.RampAnimator.DualRampAnimator; -import com.android.server.display.brightness.BrightnessEvent; -import com.android.server.display.brightness.clamper.BrightnessClamperController; -import com.android.server.display.brightness.clamper.HdrClamper; -import com.android.server.display.color.ColorDisplayService; -import com.android.server.display.config.SensorData; -import com.android.server.display.feature.DisplayManagerFlags; -import com.android.server.display.feature.flags.Flags; -import com.android.server.display.layout.Layout; -import com.android.server.display.whitebalance.DisplayWhiteBalanceController; -import com.android.server.policy.WindowManagerPolicy; -import com.android.server.testutils.OffsettableClock; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.quality.Strictness; -import org.mockito.stubbing.Answer; - -import java.util.List; - - -@SmallTest -@RunWith(AndroidJUnit4.class) -public final class DisplayPowerController2Test { - private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY; - private static final String UNIQUE_ID = "unique_id_test123"; - private static final int FOLLOWER_DISPLAY_ID = DISPLAY_ID + 1; - private static final String FOLLOWER_UNIQUE_ID = "unique_id_456"; - private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1; - private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789"; - private static final float PROX_SENSOR_MAX_RANGE = 5; - private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f; - private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f; - private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE = 0.2f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE = 0.5f; - private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE = 0.6f; - - private static final long BRIGHTNESS_RAMP_INCREASE_MAX = 1000; - private static final long BRIGHTNESS_RAMP_DECREASE_MAX = 2000; - private static final long BRIGHTNESS_RAMP_INCREASE_MAX_IDLE = 3000; - private static final long BRIGHTNESS_RAMP_DECREASE_MAX_IDLE = 4000; - - private OffsettableClock mClock; - private TestLooper mTestLooper; - private Handler mHandler; - private DisplayPowerControllerHolder mHolder; - private Sensor mProxSensor; - - @Mock - private DisplayPowerCallbacks mDisplayPowerCallbacksMock; - @Mock - private SensorManager mSensorManagerMock; - @Mock - private DisplayBlanker mDisplayBlankerMock; - @Mock - private BrightnessTracker mBrightnessTrackerMock; - @Mock - private WindowManagerPolicy mWindowManagerPolicyMock; - @Mock - private PowerManager mPowerManagerMock; - @Mock - private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock; - @Mock - private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock; - @Mock - private DisplayManagerFlags mDisplayManagerFlagsMock; - @Mock - private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; - @Captor - private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; - - @Rule - public final TestableContext mContext = new TestableContext( - InstrumentationRegistry.getInstrumentation().getContext()); - - @Rule - public final ExtendedMockitoRule mExtendedMockitoRule = - new ExtendedMockitoRule.Builder(this) - .setStrictness(Strictness.LENIENT) - .spyStatic(SystemProperties.class) - .spyStatic(BatteryStatsService.class) - .spyStatic(ActivityManager.class) - .build(); - - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - @Before - public void setUp() throws Exception { - mClock = new OffsettableClock.Stopped(); - mTestLooper = new TestLooper(mClock::now); - mHandler = new Handler(mTestLooper.getLooper()); - - // Set some settings to minimize unexpected events and have a consistent starting state - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); - Settings.System.putFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT); - - addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock); - addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class, - mCdsiMock); - - mContext.addMockSystemService(PowerManager.class, mPowerManagerMock); - - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_displayColorFadeDisabled, false); - - doAnswer((Answer<Void>) invocationOnMock -> null).when(() -> - SystemProperties.set(anyString(), any())); - doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService); - doAnswer((Answer<Boolean>) invocationOnMock -> false) - .when(ActivityManager::isLowRamDeviceStatic); - - setUpSensors(); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - } - - @After - public void tearDown() { - LocalServices.removeServiceForTest(WindowManagerPolicy.class); - LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); - } - - @Test - public void testReleaseProxSuspendBlockersOnExit() throws Exception { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState to start listener for the prox sensor - advanceTime(1); - - SensorEventListener listener = getSensorEventListener(mProxSensor); - assertNotNull(listener); - - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 5)); - advanceTime(1); - - // two times, one for unfinished business and one for proximity - verify(mHolder.wakelockController, times(2)).acquireWakelock( - WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - verify(mHolder.wakelockController).acquireWakelock( - WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); - - mHolder.dpc.stop(); - advanceTime(1); - // two times, one for unfinished business and one for proximity - verify(mHolder.wakelockController, times(2)).acquireWakelock( - WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - verify(mHolder.wakelockController).acquireWakelock( - WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); - } - - @Test - public void testScreenOffBecauseOfProximity() throws Exception { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState to start listener for the prox sensor - advanceTime(1); - - SensorEventListener listener = getSensorEventListener(mProxSensor); - assertNotNull(listener); - - // Send a positive proximity event - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 1)); - advanceTime(1); - - // The display should have been turned off - verify(mHolder.displayPowerState).setScreenState(Display.STATE_OFF); - - clearInvocations(mHolder.displayPowerState); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - // Send a negative proximity event - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, - (int) PROX_SENSOR_MAX_RANGE + 1)); - // Advance time by less than PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY - advanceTime(1); - - // The prox sensor is debounced so the display should not have been turned back on yet - verify(mHolder.displayPowerState, never()).setScreenState(Display.STATE_ON); - - // Advance time by more than PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY - advanceTime(1000); - - // The display should have been turned back on - verify(mHolder.displayPowerState).setScreenState(Display.STATE_ON); - } - - @Test - public void testScreenOffBecauseOfProximity_ProxSensorGone() throws Exception { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState to start listener for the prox sensor - advanceTime(1); - - SensorEventListener listener = getSensorEventListener(mProxSensor); - assertNotNull(listener); - - // Send a positive proximity event - listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, /* value= */ 1)); - advanceTime(1); - - // The display should have been turned off - verify(mHolder.displayPowerState).setScreenState(Display.STATE_OFF); - - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - // The display device changes and we no longer have a prox sensor - reset(mSensorManagerMock); - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - - advanceTime(1); // Run updatePowerState - - // The display should have been turned back on and the listener should have been - // unregistered - verify(mHolder.displayPowerState).setScreenState(Display.STATE_ON); - verify(mSensorManagerMock).unregisterListener(listener); - } - - @Test - public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() { - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - // send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - final DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - followerDpc.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - verify(mSensorManagerMock, never()).registerListener(any(SensorEventListener.class), - eq(mProxSensor), anyInt(), any(Handler.class)); - } - - @Test - public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - // Test different float scale values - float leadBrightness = 0.3f; - float followerBrightness = 0.4f; - float nits = 300; - when(mHolder.automaticBrightnessController.convertToNits(leadBrightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(followerBrightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness); - listener.onBrightnessChanged(leadBrightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false)); - verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), - anyFloat(), eq(false)); - - clearInvocations(mHolder.animator, followerDpc.animator); - - // Test the same float scale value - float brightness = 0.6f; - nits = 600; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - float brightness = 0.3f; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(300f); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) - .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - float brightness = 0.3f; - when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - float brightness = 0.3f; - when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) - .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowers_AutomaticBrightness() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - float leadBrightness = 0.1f; - float rawLeadBrightness = 0.3f; - float followerBrightness = 0.4f; - float nits = 300; - float ambientLux = 3000; - when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) - .thenReturn(rawLeadBrightness); - when(mHolder.automaticBrightnessController - .getAutomaticScreenBrightness(any(BrightnessEvent.class))) - .thenReturn(leadBrightness); - when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) - .thenReturn(nits); - when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(followerBrightness); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - // One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow - verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux); - verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness); - when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness); - clearInvocations(mHolder.animator, followerDpc.animator); - - leadBrightness = 0.05f; - rawLeadBrightness = 0.2f; - followerBrightness = 0.3f; - nits = 200; - ambientLux = 2000; - when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) - .thenReturn(rawLeadBrightness); - when(mHolder.automaticBrightnessController - .getAutomaticScreenBrightness(any(BrightnessEvent.class))) - .thenReturn(leadBrightness); - when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) - .thenReturn(nits); - when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(followerBrightness); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - // The second time, the animation rate should be slow - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false)); - verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux); - verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() { - DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, - FOLLOWER_UNIQUE_ID); - DisplayPowerControllerHolder secondFollowerDpc = createDisplayPowerController( - SECOND_FOLLOWER_DISPLAY_ID, SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(secondFollowerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - secondFollowerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - // Set the initial brightness on the DPC we're going to remove so we have a fixed value for - // it to return to. - listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(followerDpc.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue(); - final float initialFollowerBrightness = 0.3f; - when(followerDpc.brightnessSetting.getBrightness()).thenReturn(initialFollowerBrightness); - followerListener.onBrightnessChanged(initialFollowerBrightness); - advanceTime(1); - verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(followerDpc.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - - mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - mHolder.dpc.addDisplayBrightnessFollower(secondFollowerDpc.dpc); - clearInvocations(followerDpc.animator); - - // Validate both followers are correctly registered and receiving brightness updates - float brightness = 0.6f; - float nits = 600; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(secondFollowerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); - clearInvocations(mHolder.animator, followerDpc.animator, secondFollowerDpc.animator); - - // Remove the first follower and validate it goes back to its original brightness. - mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc); - advanceTime(1); - verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false)); - - when(followerDpc.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - clearInvocations(followerDpc.animator); - - // Change the brightness of the lead display and validate only the second follower responds - brightness = 0.7f; - nits = 700; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat(), - anyBoolean()); - verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() { - DisplayPowerControllerHolder followerHolder = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - DisplayPowerControllerHolder secondFollowerHolder = - createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID, - SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(followerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(secondFollowerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = - ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - - // Set the initial brightness on the DPCs we're going to remove so we have a fixed value for - // it to return to. - listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue(); - listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(secondFollowerHolder.brightnessSetting).registerListener(listenerCaptor.capture()); - BrightnessSetting.BrightnessSettingListener secondFollowerListener = - listenerCaptor.getValue(); - final float initialFollowerBrightness = 0.3f; - when(followerHolder.brightnessSetting.getBrightness()).thenReturn( - initialFollowerBrightness); - when(secondFollowerHolder.brightnessSetting.getBrightness()).thenReturn( - initialFollowerBrightness); - followerListener.onBrightnessChanged(initialFollowerBrightness); - secondFollowerListener.onBrightnessChanged(initialFollowerBrightness); - advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(followerHolder.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - when(secondFollowerHolder.displayPowerState.getScreenBrightness()) - .thenReturn(initialFollowerBrightness); - - mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc); - mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc); - clearInvocations(followerHolder.animator, secondFollowerHolder.animator); - - // Validate both followers are correctly registered and receiving brightness updates - float brightness = 0.6f; - float nits = 600; - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(secondFollowerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(brightness); - when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); - listener.onBrightnessChanged(brightness); - advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - when(secondFollowerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator); - - // Stop the lead DPC and validate that the followers go back to their original brightness. - mHolder.dpc.stop(); - advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false)); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false)); - clearInvocations(followerHolder.animator, secondFollowerHolder.animator); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); - - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - clearInvocations(mHolder.animator); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); - - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); - - clearInvocations(mHolder.animator); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); - } - - @Test - public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() { - // We should still set screen state for the default display - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - verify(mHolder.displayPowerState, times(2)).setScreenState(anyInt()); - - mHolder = createDisplayPowerController(42, UNIQUE_ID); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); - - mHolder.dpc.onBootCompleted(); - advanceTime(1); // Run updatePowerState - verify(mHolder.displayPowerState).setScreenState(anyInt()); - } - - @Test - public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(true); - - // The display turns on and we use the brightness value recommended by - // ScreenOffBrightnessSensorController - clearInvocations(mHolder.screenOffBrightnessSensorController); - float brightness = 0.14f; - when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness()) - .thenReturn(brightness); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .getAutomaticScreenBrightness(); - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false)); - } - - @Test - public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() { - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(true); - - // The display turns on and we use the brightness value recommended by - // ScreenOffBrightnessSensorController - clearInvocations(mHolder.screenOffBrightnessSensorController); - float brightness = 0.14f; - when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness()) - .thenReturn(brightness); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .getAutomaticScreenBrightness(); - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat(), eq(false)); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() { - // Tests are set up with manual brightness by default, so no need to set it here. - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_DisplayIsOn() { - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testSetScreenOffBrightnessSensorDisabled_DisplayIsAFollower() { - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, /* leadDisplayId= */ 42); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController, atLeastOnce()) - .setLightSensorEnabled(false); - } - - @Test - public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() { - // New display device - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.screenOffBrightnessSensorController).stop(); - } - - @Test - public void testAutoBrightnessEnabled_DisplayIsOn() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); - } - - @Test - public void testAutoBrightnessEnabled_DisplayIsInDoze() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); - } - - @Test - public void testAutoBrightnessDisabled_ManualBrightnessMode() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by the test, the other by handleBrightnessModeChange - verify(mHolder.automaticBrightnessController, times(2)).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController, times(2)) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); - } - - @Test - public void testAutoBrightnessDisabled_DisplayIsOff() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController).setAutoBrightnessEnabled( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); - } - - @Test - public void testAutoBrightnessDisabled_DisplayIsInDoze() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, - /* shouldResetShortTermModel= */ false - ); - verify(mHolder.hbmController).setAutoBrightnessEnabled( - AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); - } - - @Test - public void testAutoBrightnessDisabled_FollowerDisplay() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mHolder.dpc.setBrightnessToFollow(0.3f, -1, 0, /* slowChange= */ false); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by the test, the other by handleBrightnessModeChange - verify(mHolder.automaticBrightnessController, times(2)).configure( - AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, - /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, - /* userChangedBrightness= */ false, /* adjustment= */ 0, - /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, - /* shouldResetShortTermModel= */ false - ); - - // HBM should be allowed for the follower display - verify(mHolder.hbmController) - .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); - } - - @Test - public void testBrightnessNitsPersistWhenDisplayDeviceChanges() { - float brightness = 0.3f; - float nits = 500; - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay, - true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - - mHolder.dpc.setBrightness(brightness); - verify(mHolder.brightnessSetting).setBrightnessNitsForDefaultDisplay(nits); - - float newBrightness = 0.4f; - when(mHolder.brightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits); - when(mHolder.automaticBrightnessController.getBrightnessFromNits(nits)) - .thenReturn(newBrightness); - // New display device - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - // One triggered by handleBrightnessModeChange, another triggered by onDisplayChanged - verify(mHolder.animator, times(2)).animateTo(eq(newBrightness), anyFloat(), anyFloat(), - eq(false)); - } - - @Test - public void testShortTermModelPersistsWhenDisplayDeviceChanges() { - float lux = 2000; - float nits = 500; - when(mHolder.automaticBrightnessController.getUserLux()).thenReturn(lux); - when(mHolder.automaticBrightnessController.getUserNits()).thenReturn(nits); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); - clearInvocations(mHolder.injector); - - // New display device - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mock(DisplayDeviceConfig.class), /* isEnabled= */ true); - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - advanceTime(1); - - verify(mHolder.injector).getAutomaticBrightnessController( - any(AutomaticBrightnessController.Callbacks.class), - any(Looper.class), - eq(mSensorManagerMock), - /* lightSensor= */ any(), - /* brightnessMappingStrategyMap= */ any(SparseArray.class), - /* lightSensorWarmUpTime= */ anyInt(), - /* brightnessMin= */ anyFloat(), - /* brightnessMax= */ anyFloat(), - /* dozeScaleFactor */ anyFloat(), - /* lightSensorRate= */ anyInt(), - /* initialLightSensorRate= */ anyInt(), - /* brighteningLightDebounceConfig */ anyLong(), - /* darkeningLightDebounceConfig */ anyLong(), - /* brighteningLightDebounceConfigIdle= */ anyLong(), - /* darkeningLightDebounceConfigIdle= */ anyLong(), - /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(), - any(HysteresisLevels.class), - any(HysteresisLevels.class), - any(HysteresisLevels.class), - any(HysteresisLevels.class), - eq(mContext), - any(BrightnessRangeController.class), - any(BrightnessThrottler.class), - /* ambientLightHorizonShort= */ anyInt(), - /* ambientLightHorizonLong= */ anyInt(), - eq(lux), - eq(nits) - ); - } - - @Test - public void testUpdateBrightnessThrottlingDataId() { - mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId = - "throttling-data-id"; - clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()); - - mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()) - .getThermalBrightnessThrottlingDataMapByThrottlingId(); - } - - @Test - public void testSetBrightness_BrightnessShouldBeClamped() { - float clampedBrightness = 0.6f; - when(mHolder.hbmController.getCurrentBrightnessMax()).thenReturn(clampedBrightness); - - mHolder.dpc.setBrightness(PowerManager.BRIGHTNESS_MAX); - - verify(mHolder.brightnessSetting).setBrightness(clampedBrightness); - } - - @Test - public void testDwbcCallsHappenOnHandler() { - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true); - - // dispatch handler looper - advanceTime(1); - verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true); - } - - @Test - public void testRampRatesIdle() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - float brightness = 0.6f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(brightness); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); - brightness = 0.05f; - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(brightness); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - - // The second time, the animation rate should be slow - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE), eq(false)); - - brightness = 0.9f; - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(brightness); - - mHolder.dpc.updateBrightness(); - advanceTime(1); // Run updatePowerState - // The third time, the animation rate should be slow - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE), eq(false)); - } - - @Test - public void testRampRateForHdrContent_HdrClamperOff() { - float hdrBrightness = 0.8f; - float clampedBrightness = 0.6f; - float transitionRate = 1.5f; - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); - when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testRampRateForHdrContent_HdrClamperOn() { - float clampedBrightness = 0.6f; - float transitionRate = 1.5f; - when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); - when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(), - eq(transitionRate), eq(true)); - } - - @Test - public void testRampRateForClampersControllerApplied() { - float transitionRate = 1.5f; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( - invocation -> DisplayBrightnessState.builder() - .setIsSlowChange(invocation.getArgument(2)) - .setBrightness(invocation.getArgument(1)) - .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) - .setCustomAnimationRate(transitionRate).build()); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), - eq(transitionRate), anyBoolean()); - } - - @Test - public void testRampRateForClampersControllerNotApplied_ifDoze() { - float transitionRate = 1.5f; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - dpr.dozeScreenState = Display.STATE_UNKNOWN; - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( - invocation -> DisplayBrightnessState.builder() - .setIsSlowChange(invocation.getArgument(2)) - .setBrightness(invocation.getArgument(1)) - .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) - .setCustomAnimationRate(transitionRate).build()); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), anyBoolean()); - verify(mHolder.animator, never()).animateTo(anyFloat(), anyFloat(), - eq(transitionRate), anyBoolean()); - } - - @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeInteractiveThenIdle() { - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - advanceTime(1); - - // A second time, when switching to idle mode. - verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeInteractiveThenIdle_DifferentValues() { - when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); - - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - advanceTime(1); - - // A second time, when switching to idle mode. - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, - BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); - } - - @Test - @RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeIdle() { - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - // Run updatePowerState - advanceTime(1); - // Once on setup - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - - // A second time when switching to idle mode. - verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, - BRIGHTNESS_RAMP_DECREASE_MAX); - } - - @Test - @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) - public void testRampMaxTimeIdle_DifferentValues() { - when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); - - // Send a display power request - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - dpr.useProximitySensor = true; - mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - - // Run updatePowerState - advanceTime(1); - - setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), - mHolder.config, /* isEnabled= */ true); - - // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, - BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); - } - - @Test - public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() { - // set up. - int initState = Display.STATE_DOZE; - int supportedTargetState = Display.STATE_DOZE_SUSPEND; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - doAnswer(invocation -> { - when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); - return null; - }).when(mHolder.displayPowerState).setScreenState(anyInt()); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - // start with DOZE. - when(mHolder.displayPowerState.getScreenState()).thenReturn(initState); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - mHolder.dpc.overrideDozeScreenState(supportedTargetState); - advanceTime(1); // Run updatePowerState - - verify(mHolder.displayPowerState).setScreenState(supportedTargetState); - } - - @Test - public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() { - // set up. - int initState = Display.STATE_DOZE; - int unSupportedTargetState = Display.STATE_ON; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - doAnswer(invocation -> { - when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); - return null; - }).when(mHolder.displayPowerState).setScreenState(anyInt()); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - // start with DOZE. - when(mHolder.displayPowerState.getScreenState()).thenReturn(initState); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - mHolder.dpc.overrideDozeScreenState(unSupportedTargetState); - advanceTime(1); // Run updatePowerState - - verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); - } - - @Test - public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() { - // set up. - int initState = Display.STATE_OFF; - int supportedTargetState = Display.STATE_DOZE_SUSPEND; - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - doAnswer(invocation -> { - when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0)); - return null; - }).when(mHolder.displayPowerState).setScreenState(anyInt()); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - // start with OFF. - when(mHolder.displayPowerState.getScreenState()).thenReturn(initState); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_OFF; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - mHolder.dpc.overrideDozeScreenState(supportedTargetState); - advanceTime(1); // Run updatePowerState - - verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); - } - - @Test - public void testBrightnessFromOffload() { - when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - float brightness = 0.34f; - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); - mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); - - mHolder.dpc.setBrightnessFromOffload(brightness); - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by handleBrightnessModeChange, another triggered by - // setBrightnessFromOffload - verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test - public void testSwitchToDozeAutoBrightnessMode() { - when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - dpr.policy = DisplayPowerRequest.POLICY_DOZE; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - // One triggered by handleBrightnessModeChange, another triggered by requestPowerState - verify(mHolder.automaticBrightnessController, times(2)) - .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); - - // Back to default mode - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); - } - - @Test - public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() { - when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController, never()) - .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); - } - - @Test - public void testDoesNotSwitchDozeAutoBrightnessModeIfFeatureFlagOff() { - when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(false); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.automaticBrightnessController, never()) - .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); - } - - /** - * Creates a mock and registers it to {@link LocalServices}. - */ - private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { - LocalServices.removeServiceForTest(clazz); - LocalServices.addService(clazz, mock); - } - - private void advanceTime(long timeMs) { - mClock.fastForward(timeMs); - mTestLooper.dispatchAll(); - } - - private void setUpSensors() throws Exception { - mProxSensor = TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, - PROX_SENSOR_MAX_RANGE); - Sensor screenOffBrightnessSensor = TestUtils.createSensor( - Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); - when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL))) - .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor)); - } - - private SensorEventListener getSensorEventListener(Sensor sensor) { - verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(), - eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class)); - return mSensorEventListenerCaptor.getValue(); - } - - private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, - DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock, - boolean isEnabled) { - DisplayInfo info = new DisplayInfo(); - DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - deviceInfo.uniqueId = uniqueId; - - when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); - when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); - when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); - when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); - when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); - when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); - when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); - when(displayDeviceConfigMock.getProximitySensor()).thenReturn( - new SensorData(Sensor.STRING_TYPE_PROXIMITY, null)); - when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); - when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); - when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( - new SensorData()); - when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( - new SensorData(Sensor.STRING_TYPE_LIGHT, null)); - when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) - .thenReturn(new int[0]); - - when(displayDeviceConfigMock.getBrightnessRampFastDecrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE); - when(displayDeviceConfigMock.getBrightnessRampFastIncrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_INCREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowDecrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowIncrease()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowIncreaseIdle()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE); - when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE); - - when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis()) - .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX); - when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxMillis()) - .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX); - when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxIdleMillis()) - .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE); - when(displayDeviceConfigMock.getBrightnessRampDecreaseMaxIdleMillis()) - .thenReturn(BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); - } - - private DisplayPowerControllerHolder createDisplayPowerController(int displayId, - String uniqueId) { - return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true); - } - - private DisplayPowerControllerHolder createDisplayPowerController(int displayId, - String uniqueId, boolean isEnabled) { - final DisplayPowerState displayPowerState = mock(DisplayPowerState.class); - final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class); - final AutomaticBrightnessController automaticBrightnessController = - mock(AutomaticBrightnessController.class); - final WakelockController wakelockController = mock(WakelockController.class); - final BrightnessMappingStrategy brightnessMappingStrategy = - mock(BrightnessMappingStrategy.class); - final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class); - final ScreenOffBrightnessSensorController screenOffBrightnessSensorController = - mock(ScreenOffBrightnessSensorController.class); - final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class); - final HdrClamper hdrClamper = mock(HdrClamper.class); - BrightnessClamperController clamperController = mock(BrightnessClamperController.class); - - when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); - when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( - invocation -> DisplayBrightnessState.builder() - .setIsSlowChange(invocation.getArgument(2)) - .setBrightness(invocation.getArgument(1)) - .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) - .setCustomAnimationRate(-1).build()); - - TestInjector injector = spy(new TestInjector(displayPowerState, animator, - automaticBrightnessController, wakelockController, brightnessMappingStrategy, - hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper, - clamperController, mDisplayManagerFlagsMock)); - - final LogicalDisplay display = mock(LogicalDisplay.class); - final DisplayDevice device = mock(DisplayDevice.class); - final HighBrightnessModeMetadata hbmMetadata = mock(HighBrightnessModeMetadata.class); - final BrightnessSetting brightnessSetting = mock(BrightnessSetting.class); - final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class); - - setUpDisplay(displayId, uniqueId, display, device, config, isEnabled); - - final DisplayPowerController2 dpc = new DisplayPowerController2( - mContext, injector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, display, - mBrightnessTrackerMock, brightnessSetting, () -> { - }, - hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock); - - return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, - animator, automaticBrightnessController, wakelockController, - screenOffBrightnessSensorController, hbmController, hdrClamper, clamperController, - hbmMetadata, brightnessMappingStrategy, injector, config); - } - - /** - * A class for holding a DisplayPowerController under test and all the mocks specifically - * related to it. - */ - private static class DisplayPowerControllerHolder { - public final DisplayPowerController2 dpc; - public final LogicalDisplay display; - public final DisplayPowerState displayPowerState; - public final BrightnessSetting brightnessSetting; - public final DualRampAnimator<DisplayPowerState> animator; - public final AutomaticBrightnessController automaticBrightnessController; - public final WakelockController wakelockController; - public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; - public final HighBrightnessModeController hbmController; - - public final HdrClamper hdrClamper; - public final BrightnessClamperController clamperController; - public final HighBrightnessModeMetadata hbmMetadata; - public final BrightnessMappingStrategy brightnessMappingStrategy; - public final DisplayPowerController2.Injector injector; - public final DisplayDeviceConfig config; - - DisplayPowerControllerHolder(DisplayPowerController2 dpc, LogicalDisplay display, - DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting, - DualRampAnimator<DisplayPowerState> animator, - AutomaticBrightnessController automaticBrightnessController, - WakelockController wakelockController, - ScreenOffBrightnessSensorController screenOffBrightnessSensorController, - HighBrightnessModeController hbmController, - HdrClamper hdrClamper, - BrightnessClamperController clamperController, - HighBrightnessModeMetadata hbmMetadata, - BrightnessMappingStrategy brightnessMappingStrategy, - DisplayPowerController2.Injector injector, - DisplayDeviceConfig config) { - this.dpc = dpc; - this.display = display; - this.displayPowerState = displayPowerState; - this.brightnessSetting = brightnessSetting; - this.animator = animator; - this.automaticBrightnessController = automaticBrightnessController; - this.wakelockController = wakelockController; - this.screenOffBrightnessSensorController = screenOffBrightnessSensorController; - this.hbmController = hbmController; - this.hdrClamper = hdrClamper; - this.clamperController = clamperController; - this.hbmMetadata = hbmMetadata; - this.brightnessMappingStrategy = brightnessMappingStrategy; - this.injector = injector; - this.config = config; - } - } - - private class TestInjector extends DisplayPowerController2.Injector { - private final DisplayPowerState mDisplayPowerState; - private final DualRampAnimator<DisplayPowerState> mAnimator; - private final AutomaticBrightnessController mAutomaticBrightnessController; - private final WakelockController mWakelockController; - private final BrightnessMappingStrategy mBrightnessMappingStrategy; - private final HysteresisLevels mHysteresisLevels; - private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; - private final HighBrightnessModeController mHighBrightnessModeController; - - private final HdrClamper mHdrClamper; - - private final BrightnessClamperController mClamperController; - - private final DisplayManagerFlags mFlags; - - TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator, - AutomaticBrightnessController automaticBrightnessController, - WakelockController wakelockController, - BrightnessMappingStrategy brightnessMappingStrategy, - HysteresisLevels hysteresisLevels, - ScreenOffBrightnessSensorController screenOffBrightnessSensorController, - HighBrightnessModeController highBrightnessModeController, - HdrClamper hdrClamper, - BrightnessClamperController clamperController, - DisplayManagerFlags flags) { - mDisplayPowerState = dps; - mAnimator = animator; - mAutomaticBrightnessController = automaticBrightnessController; - mWakelockController = wakelockController; - mBrightnessMappingStrategy = brightnessMappingStrategy; - mHysteresisLevels = hysteresisLevels; - mScreenOffBrightnessSensorController = screenOffBrightnessSensorController; - mHighBrightnessModeController = highBrightnessModeController; - mHdrClamper = hdrClamper; - mClamperController = clamperController; - mFlags = flags; - } - - @Override - DisplayPowerController2.Clock getClock() { - return mClock::now; - } - - @Override - DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, - int displayId, int displayState) { - return mDisplayPowerState; - } - - @Override - DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, - FloatProperty<DisplayPowerState> firstProperty, - FloatProperty<DisplayPowerState> secondProperty) { - return mAnimator; - } - - @Override - WakelockController getWakelockController(int displayId, - DisplayPowerCallbacks displayPowerCallbacks) { - return mWakelockController; - } - - @Override - DisplayPowerProximityStateController getDisplayPowerProximityStateController( - WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, - Looper looper, Runnable nudgeUpdatePowerState, int displayId, - SensorManager sensorManager) { - return new DisplayPowerProximityStateController(wakelockController, - displayDeviceConfig, looper, nudgeUpdatePowerState, displayId, - sensorManager, - new DisplayPowerProximityStateController.Injector() { - @Override - DisplayPowerProximityStateController.Clock createClock() { - return mClock::now; - } - }); - } - - @Override - AutomaticBrightnessController getAutomaticBrightnessController( - AutomaticBrightnessController.Callbacks callbacks, Looper looper, - SensorManager sensorManager, Sensor lightSensor, - SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, - int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, - float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, - long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, - long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle, - boolean resetAmbientLuxAfterWarmUpConfig, - HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, - HysteresisLevels ambientBrightnessThresholdsIdle, - HysteresisLevels screenBrightnessThresholdsIdle, Context context, - BrightnessRangeController brightnessRangeController, - BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userNits) { - return mAutomaticBrightnessController; - } - - @Override - BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, - DisplayDeviceConfig displayDeviceConfig, - DisplayWhiteBalanceController displayWhiteBalanceController) { - return mBrightnessMappingStrategy; - } - - @Override - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold) { - return mHysteresisLevels; - } - - @Override - HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, - float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, - float[] darkeningThresholdLevels, float minDarkeningThreshold, - float minBrighteningThreshold, boolean potentialOldBrightnessRange) { - return mHysteresisLevels; - } - - @Override - ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController( - SensorManager sensorManager, Sensor lightSensor, Handler handler, - ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux, - BrightnessMappingStrategy brightnessMapper) { - return mScreenOffBrightnessSensorController; - } - - @Override - HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, - int height, IBinder displayToken, String displayUniqueId, float brightnessMin, - float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, - HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, - Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, - Context context) { - return mHighBrightnessModeController; - } - - @Override - BrightnessRangeController getBrightnessRangeController( - HighBrightnessModeController hbmController, Runnable modeChangeCallback, - DisplayDeviceConfig displayDeviceConfig, Handler handler, - DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { - return new BrightnessRangeController(hbmController, modeChangeCallback, - displayDeviceConfig, mHdrClamper, mFlags, displayToken, info); - } - - @Override - BrightnessClamperController getBrightnessClamperController(Handler handler, - BrightnessClamperController.ClamperChangeListener clamperChangeListener, - BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - return mClamperController; - } - - @Override - DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, - SensorManager sensorManager, Resources resources) { - return mDisplayWhiteBalanceControllerMock; - } - } -} diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 943862f918bc..88a9758991f4 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -18,6 +18,8 @@ package com.android.server.display; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; import static org.junit.Assert.assertNotNull; @@ -67,15 +69,16 @@ import android.view.Display; import android.view.DisplayInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.internal.util.test.LocalServiceKeeperRule; import com.android.modules.utils.testing.ExtendedMockitoRule; +import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.brightness.BrightnessEvent; +import com.android.server.display.brightness.clamper.BrightnessClamperController; +import com.android.server.display.brightness.clamper.HdrClamper; import com.android.server.display.color.ColorDisplayService; import com.android.server.display.config.SensorData; import com.android.server.display.feature.DisplayManagerFlags; @@ -85,6 +88,7 @@ import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.policy.WindowManagerPolicy; import com.android.server.testutils.OffsettableClock; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -147,7 +151,6 @@ public final class DisplayPowerControllerTest { private DisplayManagerFlags mDisplayManagerFlagsMock; @Mock private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; - @Captor private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; @@ -165,9 +168,6 @@ public final class DisplayPowerControllerTest { .build(); @Rule - public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule(); - - @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before @@ -183,10 +183,9 @@ public final class DisplayPowerControllerTest { Settings.System.putFloatForUser(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT); - mLocalServiceKeeperRule.overrideLocalService( - WindowManagerPolicy.class, mWindowManagerPolicyMock); - mLocalServiceKeeperRule.overrideLocalService( - ColorDisplayService.ColorDisplayServiceInternal.class, mCdsiMock); + addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock); + addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class, + mCdsiMock); mContext.addMockSystemService(PowerManager.class, mPowerManagerMock); @@ -203,6 +202,12 @@ public final class DisplayPowerControllerTest { mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); } + @After + public void tearDown() { + LocalServices.removeServiceForTest(WindowManagerPolicy.class); + LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); + } + @Test public void testReleaseProxSuspendBlockersOnExit() throws Exception { when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); @@ -222,19 +227,18 @@ public final class DisplayPowerControllerTest { advanceTime(1); // two times, one for unfinished business and one for proximity - verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( - mHolder.dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); - verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( - mHolder.dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + verify(mHolder.wakelockController, times(2)).acquireWakelock( + WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); + verify(mHolder.wakelockController).acquireWakelock( + WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); mHolder.dpc.stop(); advanceTime(1); - // two times, one for unfinished business and one for proximity - verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( - mHolder.dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); - verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( - mHolder.dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + verify(mHolder.wakelockController, times(2)).acquireWakelock( + WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); + verify(mHolder.wakelockController).acquireWakelock( + WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); } @Test @@ -316,14 +320,13 @@ public final class DisplayPowerControllerTest { @Test public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() { - DisplayPowerControllerHolder followerDpc = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); // send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; + final DisplayPowerControllerHolder followerDpc = + createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); followerDpc.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); // Run updatePowerState @@ -334,7 +337,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -363,13 +365,10 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness); listener.onBrightnessChanged(leadBrightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat(), eq(false)); verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + anyFloat(), eq(false)); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness); clearInvocations(mHolder.animator, followerDpc.animator); // Test the same float scale value @@ -388,7 +387,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -421,7 +419,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -452,7 +449,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -485,7 +481,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowers_AutomaticBrightness() { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, @@ -557,7 +552,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -650,7 +644,6 @@ public final class DisplayPowerControllerTest { } @Test - @FlakyTest(bugId = 294107062) public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() { DisplayPowerControllerHolder followerHolder = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); @@ -737,6 +730,82 @@ public final class DisplayPowerControllerTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) + public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + final float sdrBrightness = 0.1f; + final float hdrBrightness = 0.3f; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(sdrBrightness); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); + + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); + clearInvocations(mHolder.animator); + + mHolder.dpc.updateBrightness(); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) + public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + final float sdrBrightness = 0.1f; + final float hdrBrightness = 0.3f; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(sdrBrightness); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(1.0f); + + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); + + clearInvocations(mHolder.animator); + + mHolder.dpc.updateBrightness(); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), + eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + } + + @Test public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() { // We should still set screen state for the default display DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -761,6 +830,8 @@ public final class DisplayPowerControllerTest { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); + DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_OFF; mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -798,6 +869,7 @@ public final class DisplayPowerControllerTest { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_DOZE; mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -842,7 +914,6 @@ public final class DisplayPowerControllerTest { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false); DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -1048,7 +1119,6 @@ public final class DisplayPowerControllerTest { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay, true); - mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); @@ -1199,76 +1269,98 @@ public final class DisplayPowerControllerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); + public void testRampRateForHdrContent_HdrClamperOff() { + float hdrBrightness = 0.8f; + float clampedBrightness = 0.6f; + float transitionRate = 1.5f; DisplayPowerRequest dpr = new DisplayPowerRequest(); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); + when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); + when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), + verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(), eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + } - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); + @Test + public void testRampRateForHdrContent_HdrClamperOn() { + float clampedBrightness = 0.6f; + float transitionRate = 1.5f; + when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - clearInvocations(mHolder.animator); + when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX); + when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); + when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); - mHolder.dpc.updateBrightness(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(), + eq(transitionRate), eq(true)); } @Test - @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS) - public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float sdrBrightness = 0.1f; - final float hdrBrightness = 0.3f; - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + public void testRampRateForClampersControllerApplied() { + float transitionRate = 1.5f; + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + DisplayPowerRequest dpr = new DisplayPowerRequest(); when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( - any(BrightnessEvent.class))).thenReturn(sdrBrightness); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + invocation -> DisplayBrightnessState.builder() + .setIsSlowChange(invocation.getArgument(2)) + .setBrightness(invocation.getArgument(1)) + .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) + .setCustomAnimationRate(transitionRate).build()); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - - DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(hdrBrightness); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(sdrBrightness); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); + verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), + eq(transitionRate), anyBoolean()); + } - clearInvocations(mHolder.animator); + @Test + public void testRampRateForClampersControllerNotApplied_ifDoze() { + float transitionRate = 1.5f; + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + dpr.dozeScreenState = Display.STATE_UNKNOWN; + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); + when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + invocation -> DisplayBrightnessState.builder() + .setIsSlowChange(invocation.getArgument(2)) + .setBrightness(invocation.getArgument(1)) + .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) + .setCustomAnimationRate(transitionRate).build()); - mHolder.dpc.updateBrightness(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness), - eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false)); + verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), anyBoolean()); + verify(mHolder.animator, never()).animateTo(anyFloat(), anyFloat(), + eq(transitionRate), anyBoolean()); } @Test @@ -1285,14 +1377,14 @@ public final class DisplayPowerControllerTest { setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); - // switch to idle + // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); + // A second time, when switching to idle mode. verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); } @@ -1301,6 +1393,8 @@ public final class DisplayPowerControllerTest { @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) public void testRampMaxTimeInteractiveThenIdle_DifferentValues() { when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); + // Send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; @@ -1312,14 +1406,14 @@ public final class DisplayPowerControllerTest { setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), mHolder.config, /* isEnabled= */ true); - verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); - // switch to idle + // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); + // A second time, when switching to idle mode. verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); } @@ -1332,11 +1426,9 @@ public final class DisplayPowerControllerTest { dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); - // Run updatePowerState advanceTime(1); - - // once on setup + // Once on setup verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); @@ -1346,7 +1438,7 @@ public final class DisplayPowerControllerTest { // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - // second time when switching to idle screen brightness mode + // A second time when switching to idle mode. verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); } @@ -1355,6 +1447,7 @@ public final class DisplayPowerControllerTest { @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1) public void testRampMaxTimeIdle_DifferentValues() { when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); // Send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -1374,6 +1467,7 @@ public final class DisplayPowerControllerTest { verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); } + @Test public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() { // set up. @@ -1451,6 +1545,89 @@ public final class DisplayPowerControllerTest { verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); } + @Test + public void testBrightnessFromOffload() { + when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + float brightness = 0.34f; + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); + + mHolder.dpc.setBrightnessFromOffload(brightness); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by handleBrightnessModeChange, another triggered by + // setBrightnessFromOffload + verify(mHolder.animator, times(2)).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + } + + @Test + public void testSwitchToDozeAutoBrightnessMode() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by handleBrightnessModeChange, another triggered by requestPowerState + verify(mHolder.automaticBrightnessController, times(2)) + .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); + + // Back to default mode + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); + } + + @Test + public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController, never()) + .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); + } + + @Test + public void testDoesNotSwitchDozeAutoBrightnessModeIfFeatureFlagOff() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(false); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController, never()) + .switchMode(AUTO_BRIGHTNESS_MODE_DOZE); + } + + /** + * Creates a mock and registers it to {@link LocalServices}. + */ + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + private void advanceTime(long timeMs) { mClock.fastForward(timeMs); mTestLooper.dispatchAll(); @@ -1505,10 +1682,10 @@ public final class DisplayPowerControllerTest { .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE); when(displayDeviceConfigMock.getBrightnessRampSlowIncrease()) .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE); - when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle()) - .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE); when(displayDeviceConfigMock.getBrightnessRampSlowIncreaseIdle()) .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE_IDLE); + when(displayDeviceConfigMock.getBrightnessRampSlowDecreaseIdle()) + .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE_IDLE); when(displayDeviceConfigMock.getBrightnessRampIncreaseMaxMillis()) .thenReturn(BRIGHTNESS_RAMP_INCREASE_MAX); @@ -1531,18 +1708,28 @@ public final class DisplayPowerControllerTest { final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class); final AutomaticBrightnessController automaticBrightnessController = mock(AutomaticBrightnessController.class); + final WakelockController wakelockController = mock(WakelockController.class); final BrightnessMappingStrategy brightnessMappingStrategy = mock(BrightnessMappingStrategy.class); final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class); final ScreenOffBrightnessSensorController screenOffBrightnessSensorController = mock(ScreenOffBrightnessSensorController.class); final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class); + final HdrClamper hdrClamper = mock(HdrClamper.class); + BrightnessClamperController clamperController = mock(BrightnessClamperController.class); when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); - - DisplayPowerController.Injector injector = spy(new TestInjector(displayPowerState, animator, - automaticBrightnessController, brightnessMappingStrategy, hysteresisLevels, - screenOffBrightnessSensorController, hbmController)); + when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + invocation -> DisplayBrightnessState.builder() + .setIsSlowChange(invocation.getArgument(2)) + .setBrightness(invocation.getArgument(1)) + .setMaxBrightness(PowerManager.BRIGHTNESS_MAX) + .setCustomAnimationRate(-1).build()); + + TestInjector injector = spy(new TestInjector(displayPowerState, animator, + automaticBrightnessController, wakelockController, brightnessMappingStrategy, + hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper, + clamperController, mDisplayManagerFlagsMock)); final LogicalDisplay display = mock(LogicalDisplay.class); final DisplayDevice device = mock(DisplayDevice.class); @@ -1555,12 +1742,14 @@ public final class DisplayPowerControllerTest { final DisplayPowerController dpc = new DisplayPowerController( mContext, injector, mDisplayPowerCallbacksMock, mHandler, mSensorManagerMock, mDisplayBlankerMock, display, - mBrightnessTrackerMock, brightnessSetting, () -> {}, + mBrightnessTrackerMock, brightnessSetting, () -> { + }, hbmMetadata, /* bootCompleted= */ false, mDisplayManagerFlagsMock); return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, - animator, automaticBrightnessController, screenOffBrightnessSensorController, - hbmController, hbmMetadata, brightnessMappingStrategy, injector, config); + animator, automaticBrightnessController, wakelockController, + screenOffBrightnessSensorController, hbmController, hdrClamper, clamperController, + hbmMetadata, brightnessMappingStrategy, injector, config); } /** @@ -1574,8 +1763,12 @@ public final class DisplayPowerControllerTest { public final BrightnessSetting brightnessSetting; public final DualRampAnimator<DisplayPowerState> animator; public final AutomaticBrightnessController automaticBrightnessController; + public final WakelockController wakelockController; public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; public final HighBrightnessModeController hbmController; + + public final HdrClamper hdrClamper; + public final BrightnessClamperController clamperController; public final HighBrightnessModeMetadata hbmMetadata; public final BrightnessMappingStrategy brightnessMappingStrategy; public final DisplayPowerController.Injector injector; @@ -1585,8 +1778,11 @@ public final class DisplayPowerControllerTest { DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, + WakelockController wakelockController, ScreenOffBrightnessSensorController screenOffBrightnessSensorController, HighBrightnessModeController hbmController, + HdrClamper hdrClamper, + BrightnessClamperController clamperController, HighBrightnessModeMetadata hbmMetadata, BrightnessMappingStrategy brightnessMappingStrategy, DisplayPowerController.Injector injector, @@ -1597,8 +1793,11 @@ public final class DisplayPowerControllerTest { this.brightnessSetting = brightnessSetting; this.animator = animator; this.automaticBrightnessController = automaticBrightnessController; + this.wakelockController = wakelockController; this.screenOffBrightnessSensorController = screenOffBrightnessSensorController; this.hbmController = hbmController; + this.hdrClamper = hdrClamper; + this.clamperController = clamperController; this.hbmMetadata = hbmMetadata; this.brightnessMappingStrategy = brightnessMappingStrategy; this.injector = injector; @@ -1610,24 +1809,39 @@ public final class DisplayPowerControllerTest { private final DisplayPowerState mDisplayPowerState; private final DualRampAnimator<DisplayPowerState> mAnimator; private final AutomaticBrightnessController mAutomaticBrightnessController; + private final WakelockController mWakelockController; private final BrightnessMappingStrategy mBrightnessMappingStrategy; private final HysteresisLevels mHysteresisLevels; private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; private final HighBrightnessModeController mHighBrightnessModeController; + private final HdrClamper mHdrClamper; + + private final BrightnessClamperController mClamperController; + + private final DisplayManagerFlags mFlags; + TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, + WakelockController wakelockController, BrightnessMappingStrategy brightnessMappingStrategy, HysteresisLevels hysteresisLevels, ScreenOffBrightnessSensorController screenOffBrightnessSensorController, - HighBrightnessModeController highBrightnessModeController) { + HighBrightnessModeController highBrightnessModeController, + HdrClamper hdrClamper, + BrightnessClamperController clamperController, + DisplayManagerFlags flags) { mDisplayPowerState = dps; mAnimator = animator; mAutomaticBrightnessController = automaticBrightnessController; + mWakelockController = wakelockController; mBrightnessMappingStrategy = brightnessMappingStrategy; mHysteresisLevels = hysteresisLevels; mScreenOffBrightnessSensorController = screenOffBrightnessSensorController; mHighBrightnessModeController = highBrightnessModeController; + mHdrClamper = hdrClamper; + mClamperController = clamperController; + mFlags = flags; } @Override @@ -1649,6 +1863,28 @@ public final class DisplayPowerControllerTest { } @Override + WakelockController getWakelockController(int displayId, + DisplayPowerCallbacks displayPowerCallbacks) { + return mWakelockController; + } + + @Override + DisplayPowerProximityStateController getDisplayPowerProximityStateController( + WakelockController wakelockController, DisplayDeviceConfig displayDeviceConfig, + Looper looper, Runnable nudgeUpdatePowerState, int displayId, + SensorManager sensorManager) { + return new DisplayPowerProximityStateController(wakelockController, + displayDeviceConfig, looper, nudgeUpdatePowerState, displayId, + sensorManager, + new DisplayPowerProximityStateController.Injector() { + @Override + DisplayPowerProximityStateController.Clock createClock() { + return mClock::now; + } + }); + } + + @Override AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, @@ -1710,6 +1946,23 @@ public final class DisplayPowerControllerTest { } @Override + BrightnessRangeController getBrightnessRangeController( + HighBrightnessModeController hbmController, Runnable modeChangeCallback, + DisplayDeviceConfig displayDeviceConfig, Handler handler, + DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) { + return new BrightnessRangeController(hbmController, modeChangeCallback, + displayDeviceConfig, mHdrClamper, mFlags, displayToken, info); + } + + @Override + BrightnessClamperController getBrightnessClamperController(Handler handler, + BrightnessClamperController.ClamperChangeListener clamperChangeListener, + BrightnessClamperController.DisplayDeviceData data, Context context, + DisplayManagerFlags flags) { + return mClamperController; + } + + @Override DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, SensorManager sensorManager, Resources resources) { return mDisplayWhiteBalanceControllerMock; diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java index c0051c6c9e17..eee37525ee37 100644 --- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java @@ -133,7 +133,8 @@ public class AnrHelperTest { verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding( eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName), eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService), - eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture)); + anyBoolean() /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, + eq(mEarlyDumpFuture)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 276c8321fb65..e1f490ae3e2f 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -341,7 +341,8 @@ public class VirtualDeviceManagerServiceTest { mSetFlagsRule.initAllFlagsToReleaseConfigDefault(); doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt()); - doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt()); + doNothing().when(mInputManagerInternalMock) + .setMousePointerAccelerationEnabled(anyBoolean(), anyInt()); doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt()); LocalServices.removeServiceForTest(InputManagerInternal.class); LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java index c10c3c28e9dd..9b25f58acc96 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java @@ -183,7 +183,6 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isFalse(); verify(mDb, times(2)).disableHistory(); } - @Test public void testAddProfile_historyEnabledInPrimary() { // create a history @@ -610,4 +609,14 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { assertThat(mHistoryManager.isHistoryEnabled(USER_SYSTEM)).isFalse(); } + @Test + public void testDelayedPackageRemoval_userLocked() { + String pkg = "pkg"; + mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg); + mHistoryManager.onUserUnlocked(USER_SYSTEM); + mHistoryManager.onUserStopped(USER_SYSTEM); + mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg); + + // no exception, yay + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java index 98f18433e53d..03d30294e1d8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java @@ -17,13 +17,31 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import android.content.ComponentName; +import android.graphics.ColorSpace; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; +import android.view.Surface; +import android.window.TaskSnapshot; import androidx.test.filters.SmallTest; @@ -32,6 +50,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.Arrays; /** * Test class for {@link ActivitySnapshotController}. @@ -45,6 +64,7 @@ import java.util.ArrayList; public class ActivitySnapshotControllerTests extends WindowTestsBase { private ActivitySnapshotController mActivitySnapshotController; + @Before public void setUp() throws Exception { spyOn(mWm.mSnapshotController.mActivitySnapshotController); @@ -154,4 +174,90 @@ public class ActivitySnapshotControllerTests extends WindowTestsBase { assertEquals(openingWindowBelow.mActivityRecord, mActivitySnapshotController.mPendingLoadActivity.valueAt(0)); } + + /** + * Simulate multiple TaskFragments inside a task. + */ + @Test + public void testMultipleActivitiesLoadSnapshot() { + final Task testTask = createTask(mDisplayContent); + final ActivityRecord activityA = createActivityRecord(testTask); + final ActivityRecord activityB = createActivityRecord(testTask); + final ActivityRecord activityC = createActivityRecord(testTask); + final TaskSnapshot taskSnapshot = createSnapshot(); + + final int[] mixedCode = new int[3]; + mixedCode[0] = ActivitySnapshotController.getSystemHashCode(activityA); + mixedCode[1] = ActivitySnapshotController.getSystemHashCode(activityB); + mixedCode[2] = ActivitySnapshotController.getSystemHashCode(activityC); + + mActivitySnapshotController.addUserSavedFile(testTask.mUserId, taskSnapshot, mixedCode); + mActivitySnapshotController.mCache.putSnapshot(activityA, taskSnapshot); + mActivitySnapshotController.mCache.putSnapshot(activityB, taskSnapshot); + mActivitySnapshotController.mCache.putSnapshot(activityC, taskSnapshot); + + assertTrue(mActivitySnapshotController.hasRecord(activityA)); + assertTrue(mActivitySnapshotController.hasRecord(activityB)); + + // If A is removed, B and C should also be removed because they share the same snapshot. + mActivitySnapshotController.onAppRemoved(activityA); + assertFalse(mActivitySnapshotController.hasRecord(activityA)); + assertFalse(mActivitySnapshotController.hasRecord(activityB)); + final ActivityRecord[] singleActivityList = new ActivityRecord[1]; + singleActivityList[0] = activityA; + assertNull(mActivitySnapshotController.getSnapshot(singleActivityList)); + singleActivityList[0] = activityB; + assertNull(mActivitySnapshotController.getSnapshot(singleActivityList)); + final ActivityRecord[] activities = new ActivityRecord[3]; + activities[0] = activityA; + activities[1] = activityB; + activities[2] = activityC; + assertNull(mActivitySnapshotController.getSnapshot(activities)); + + // Reset and test load snapshot + mActivitySnapshotController.addUserSavedFile(testTask.mUserId, taskSnapshot, mixedCode); + // Request to load by B, nothing will be loaded because the snapshot was [A,B,C]. + mActivitySnapshotController.mPendingLoadActivity.add(activityB); + mActivitySnapshotController.loadActivitySnapshot(); + verify(mActivitySnapshotController, never()).loadSnapshotInner(any(), any()); + + // Able to load snapshot when requesting for all A, B, C + mActivitySnapshotController.mPendingLoadActivity.clear(); + mActivitySnapshotController.mPendingLoadActivity.add(activityA); + mActivitySnapshotController.mPendingLoadActivity.add(activityB); + mActivitySnapshotController.mPendingLoadActivity.add(activityC); + final ArraySet<ActivityRecord> verifyList = new ArraySet<>(); + verifyList.add(activityA); + verifyList.add(activityB); + verifyList.add(activityC); + mActivitySnapshotController.loadActivitySnapshot(); + verify(mActivitySnapshotController).loadSnapshotInner(argThat( + argument -> { + final ArrayList<ActivityRecord> argumentList = new ArrayList<>( + Arrays.asList(argument)); + return verifyList.containsAll(argumentList) + && argumentList.containsAll(verifyList); + }), + any()); + + for (int i = activities.length - 1; i >= 0; --i) { + mActivitySnapshotController.mCache.putSnapshot(activities[i], taskSnapshot); + } + // The loaded snapshot can be retrieved only if the activities match exactly. + singleActivityList[0] = activityB; + assertNull(mActivitySnapshotController.getSnapshot(singleActivityList)); + assertEquals(taskSnapshot, mActivitySnapshotController.getSnapshot(activities)); + } + + private TaskSnapshot createSnapshot() { + HardwareBuffer buffer = mock(HardwareBuffer.class); + doReturn(100).when(buffer).getWidth(); + doReturn(100).when(buffer).getHeight(); + return new TaskSnapshot(1, 0 /* captureTime */, new ComponentName("", ""), buffer, + ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, + Surface.ROTATION_0, new Point(100, 100), new Rect() /* contentInsets */, + new Rect() /* letterboxInsets*/, false /* isLowResolution */, + true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */, + false /* isTranslucent */, false /* hasImeSurface */); + } } 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 d83824a39730..402cbccbca01 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -742,7 +742,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class) .strictness(Strictness.LENIENT).startMocking(); - doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any())); + doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any(), any())); when(resourcesSpy.getBoolean( com.android.internal.R.bool.config_predictShowStartingSurface)) .thenReturn(preferWindowlessSurface); diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java index 8bd54731c7c2..2d3c4bbe8bdc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java @@ -28,6 +28,7 @@ import android.os.HandlerExecutor; import android.os.Looper; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; +import android.util.Log; import androidx.test.filters.MediumTest; @@ -147,6 +148,8 @@ public class SplashScreenExceptionListTest { private void setExceptionListAndWaitForCallback(String commaSeparatedList) { CountDownLatch latch = new CountDownLatch(1); mOnUpdateDeviceConfig = rawList -> { + Log.i(getClass().getSimpleName(), "updateDeviceConfig expected=" + + commaSeparatedList + " actual=" + rawList); if (commaSeparatedList.equals(rawList)) { latch.countDown(); } @@ -155,7 +158,7 @@ public class SplashScreenExceptionListTest { KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false); try { assertTrue("Timed out waiting for DeviceConfig to be updated.", - latch.await(1, TimeUnit.SECONDS)); + latch.await(5, TimeUnit.SECONDS)); } catch (InterruptedException e) { Assert.fail(e.getMessage()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index e9fe4bb91329..22ddf8420121 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -53,6 +53,7 @@ import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; +import android.view.View; import android.window.ITaskFragmentOrganizer; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentInfo; @@ -695,4 +696,75 @@ public class TaskFragmentTest extends WindowTestsBase { mTaskFragment.getDimBounds(dimBounds); assertEquals(taskFragmentBounds, dimBounds); } + + @Test + public void testMoveFocusToAdjacentWindow() { + // Setup two activities in ActivityEmbedding split. + final Task task = createTask(mDisplayContent); + final TaskFragment taskFragmentLeft = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(2) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .build(); + final TaskFragment taskFragmentRight = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .build(); + taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight); + taskFragmentLeft.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + taskFragmentRight.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + task.setBounds(0, 0, 1200, 1000); + taskFragmentLeft.setBounds(0, 0, 600, 1000); + taskFragmentRight.setBounds(600, 0, 1200, 1000); + final ActivityRecord appLeftTop = taskFragmentLeft.getTopMostActivity(); + final ActivityRecord appLeftBottom = taskFragmentLeft.getBottomMostActivity(); + final ActivityRecord appRightTop = taskFragmentRight.getTopMostActivity(); + appLeftTop.setVisibleRequested(true); + appRightTop.setVisibleRequested(true); + final WindowState winLeftTop = createAppWindow(appLeftTop, "winLeftTop"); + final WindowState winLeftBottom = createAppWindow(appLeftBottom, "winLeftBottom"); + final WindowState winRightTop = createAppWindow(appRightTop, "winRightTop"); + winLeftTop.setHasSurface(true); + winRightTop.setHasSurface(true); + + taskFragmentLeft.setResumedActivity(appLeftTop, "test"); + taskFragmentRight.setResumedActivity(appRightTop, "test"); + appLeftTop.setState(RESUMED, "test"); + appRightTop.setState(RESUMED, "test"); + mDisplayContent.mFocusedApp = appRightTop; + + // Make the appLeftTop be the focused activity and ensure the focused app is updated. + appLeftTop.moveFocusableActivityToTop("test"); + assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); + + // Send request from a non-focused window with valid direction. + assertFalse(mWm.moveFocusToAdjacentWindow(null, winLeftBottom.mClient, View.FOCUS_RIGHT)); + // The focus should remain the same. + assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); + + // Send request from the focused window with valid direction. + assertTrue(mWm.moveFocusToAdjacentWindow(null, winLeftTop.mClient, View.FOCUS_RIGHT)); + // The focus should change. + assertEquals(winRightTop, mDisplayContent.mCurrentFocus); + + // Send request from the focused window with invalid direction. + assertFalse(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_UP)); + // The focus should remain the same. + assertEquals(winRightTop, mDisplayContent.mCurrentFocus); + + // Send request from the focused window with valid direction. + assertTrue(mWm.moveFocusToAdjacentWindow(null, winRightTop.mClient, View.FOCUS_BACKWARD)); + // The focus should change. + assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); + } + + private WindowState createAppWindow(ActivityRecord app, String name) { + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name, + 0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow()); + mWm.mWindowMap.put(win.mClient.asBinder(), win); + return win; + } } diff --git a/services/usb/lint-baseline.xml b/services/usb/lint-baseline.xml index c2c0a350d5ad..62a2ee56a0cf 100644 --- a/services/usb/lint-baseline.xml +++ b/services/usb/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -12,4 +12,4 @@ column="42"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java index 8773cabeeb92..1df7012c44f8 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java @@ -24,6 +24,7 @@ import android.telephony.TelephonyManager; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.telephony.flags.Flags; import java.util.ArrayList; import java.util.List; @@ -117,12 +118,28 @@ public class PhoneCallStateHandler { private boolean checkCallStatus() { List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList(); if (infoList == null) return false; - return infoList.stream() - .filter(s -> (s.getSubscriptionId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) - .anyMatch(s -> isCallOngoingFromState( - mTelephonyManager - .createForSubscriptionId(s.getSubscriptionId()) - .getCallStateForSubscription())); + if (!Flags.enforceTelephonyFeatureMapping()) { + return infoList.stream() + .filter(s -> (s.getSubscriptionId() + != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) + .anyMatch(s -> isCallOngoingFromState( + mTelephonyManager + .createForSubscriptionId(s.getSubscriptionId()) + .getCallStateForSubscription())); + } else { + return infoList.stream() + .filter(s -> (s.getSubscriptionId() + != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) + .anyMatch(s -> { + try { + return isCallOngoingFromState(mTelephonyManager + .createForSubscriptionId(s.getSubscriptionId()) + .getCallStateForSubscription()); + } catch (UnsupportedOperationException e) { + return false; + } + }); + } } private void updateTelephonyListeners() { diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 250c3a54c928..2a6ac98b4d98 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -196,7 +196,7 @@ public final class TelephonyPermissions { // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, callingFeatureId, null) == AppOpsManager.MODE_ALLOWED; } @@ -249,7 +249,7 @@ public final class TelephonyPermissions { // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage, callingFeatureId, null) == AppOpsManager.MODE_ALLOWED; } @@ -521,7 +521,7 @@ public final class TelephonyPermissions { // We have READ_CALL_LOG permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - return appOps.noteOp(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage, + return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage, callingPackageName, null) == AppOpsManager.MODE_ALLOWED; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c7b84a3b9530..79e4ad0575f7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -10955,6 +10955,9 @@ public class CarrierConfigManager { * @return A {@link PersistableBundle} containing the config for the given subId, or default * values for an invalid subId. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * * @deprecated Use {@link #getConfigForSubId(int, String...)} instead. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @@ -11002,6 +11005,9 @@ public class CarrierConfigManager { * @return A {@link PersistableBundle} with key/value mapping for the specified configuration * on success, or an empty (but never null) bundle on failure (for example, when the calling app * has no permission). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresPermission(anyOf = { Manifest.permission.READ_PHONE_STATE, @@ -11047,6 +11053,9 @@ public class CarrierConfigManager { * @param overrideValues Key-value pairs of the values that are to be overridden. If set to * {@code null}, this will remove all previous overrides and set the * carrier configuration back to production values. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @@ -11104,6 +11113,10 @@ public class CarrierConfigManager { * * @see #getConfigForSubId * @see #getConfig(String...) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * * @deprecated use {@link #getConfig(String...)} instead. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @@ -11138,6 +11151,9 @@ public class CarrierConfigManager { * configs on success, or an empty (but never null) bundle on failure. * @see #getConfigForSubId(int, String...) * @see SubscriptionManager#getDefaultSubscriptionId() + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @RequiresPermission(anyOf = { Manifest.permission.READ_PHONE_STATE, @@ -11189,6 +11205,9 @@ public class CarrierConfigManager { * * <p>This method returns before the reload has completed, and {@link * android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -11212,6 +11231,8 @@ public class CarrierConfigManager { * <p>Depending on simState, the config may be cleared or loaded from config app. This is only * used by SubscriptionInfoUpdater. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @SystemApi @@ -11234,6 +11255,8 @@ public class CarrierConfigManager { * Gets the package name for a default carrier service. * @return the package name for a default carrier service; empty string if not available. * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. * @hide */ @NonNull @@ -11287,6 +11310,9 @@ public class CarrierConfigManager { * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}. * * @see #getConfigForSubId + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(Manifest.permission.READ_PHONE_STATE) diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 56156024fbab..a5c6d57aed82 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1315,7 +1315,7 @@ public class SubscriptionManager { * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102), * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application. * - * <p>The availability and a of the number depends on the carrier. + * <p>The availability and accuracy of the number depends on the carrier. * The number may be updated by over-the-air update to UICC applications * from the carrier, or by other means with physical access to the SIM. */ @@ -1557,12 +1557,21 @@ public class SubscriptionManager { * caller can see all subscription across user profiles as it does today today even if it's * {@code false}. */ - private boolean mIsForAllUserProfiles = false; + private final boolean mIsForAllUserProfiles; /** @hide */ @UnsupportedAppUsage public SubscriptionManager(Context context) { - if (DBG) logd("SubscriptionManager created"); + this(context, false /*isForAllUserProfiles*/); + } + + /** Constructor */ + private SubscriptionManager(Context context, boolean isForAllUserProfiles) { + if (DBG) { + logd("SubscriptionManager created " + + (isForAllUserProfiles ? "for all user profile" : "")); + } + mIsForAllUserProfiles = isForAllUserProfiles; mContext = context; } @@ -1998,7 +2007,7 @@ public class SubscriptionManager { } /** - * Convert this subscription manager instance into one that can see all subscriptions across + * Create a new subscription manager instance that can see all subscriptions across * user profiles. * * @return a SubscriptionManager that can see all subscriptions regardless its user profile @@ -2008,13 +2017,12 @@ public class SubscriptionManager { * @see #getActiveSubscriptionInfoCount * @see UserHandle */ - @FlaggedApi(Flags.FLAG_WORK_PROFILE_API_SPLIT) + @FlaggedApi(Flags.FLAG_ENFORCE_SUBSCRIPTION_USER_FILTER) // @RequiresPermission(TODO(b/308809058)) // The permission check for accessing all subscriptions will be enforced upon calling the // individual APIs linked above. @NonNull public SubscriptionManager createForAllUserProfiles() { - mIsForAllUserProfiles = true; - return this; + return new SubscriptionManager(mContext, true/*isForAllUserProfiles*/); } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 1b47dfe0eba1..0dce0844ce64 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -18923,6 +18923,62 @@ public class TelephonyManager { } /** + * Enables or disables notifications sent when cellular null cipher or integrity algorithms + * are in use by the cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY) + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public void setEnableNullCipherNotifications(boolean enable) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setEnableNullCipherNotifications(enable); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setEnableNullCipherNotifications RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get whether notifications are enabled for null cipher or integrity algorithms in use by the + * cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY) + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public boolean isNullCipherNotificationsEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isNullCipherNotificationsEnabled(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isNullCipherNotificationsEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + + /** * Get current cell broadcast message identifier ranges. * * @throws SecurityException if the caller does not have the required permission diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 9dd83d1438e2..b6f9e1f4c3af 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -451,7 +451,7 @@ public final class DataCallResponse implements Parcelable { /** * Return the network validation status that was initiated by {@link - * DataService.DataServiceProvider#requestValidation} + * DataService.DataServiceProvider#requestNetworkValidation} * * @return The network validation status of data connection. */ @@ -931,7 +931,7 @@ public final class DataCallResponse implements Parcelable { /** * Set the network validation status that corresponds to the state of the network validation - * request started by {@link DataService.DataServiceProvider#requestValidation} + * request started by {@link DataService.DataServiceProvider#requestNetworkValidation} * * @param status The network validation status. * @return The same instance of the builder. diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 80e91a330185..f04e1c9b221d 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -415,13 +415,13 @@ public abstract class DataService extends Service { * request validation to the DataService and checks if the request has been submitted. */ @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) - public void requestValidation(int cid, + public void requestNetworkValidation(int cid, @NonNull @CallbackExecutor Executor executor, @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null"); - Log.d(TAG, "requestValidation: " + cid); + Log.d(TAG, "requestNetworkValidation: " + cid); // The default implementation is to return unsupported. executor.execute(() -> resultCodeCallback @@ -741,7 +741,7 @@ public abstract class DataService extends Service { case DATA_SERVICE_REQUEST_VALIDATION: if (serviceProvider == null) break; ValidationRequest validationRequest = (ValidationRequest) message.obj; - serviceProvider.requestValidation( + serviceProvider.requestNetworkValidation( validationRequest.cid, validationRequest.executor, FunctionalUtils @@ -924,9 +924,10 @@ public abstract class DataService extends Service { } @Override - public void requestValidation(int slotIndex, int cid, IIntegerConsumer resultCodeCallback) { + public void requestNetworkValidation(int slotIndex, int cid, + IIntegerConsumer resultCodeCallback) { if (resultCodeCallback == null) { - loge("requestValidation: resultCodeCallback is null"); + loge("requestNetworkValidation: resultCodeCallback is null"); return; } ValidationRequest validationRequest = diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 15f88815ec6b..c36c302ee986 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -48,5 +48,5 @@ oneway interface IDataService void cancelHandover(int slotId, int cid, IDataServiceCallback callback); void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback); void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback); - void requestValidation(int slotId, int cid, IIntegerConsumer callback); + void requestNetworkValidation(int slotId, int cid, IIntegerConsumer callback); } diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index 54ceaed617ec..9f83da930221 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -84,7 +84,7 @@ public interface RegistrationManager { SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK, SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT, SUGGESTED_ACTION_TRIGGER_RAT_BLOCK, - SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK + SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS }) @Retention(RetentionPolicy.SOURCE) public @interface SuggestedAction {} @@ -116,9 +116,10 @@ public interface RegistrationManager { /** * Indicates that the IMS registration on current RAT failed multiple times. - * The radio shall block the current RAT and search for other available RATs in the - * background. If no other RAT is available that meets the carrier requirements, the - * radio may remain on the current RAT for internet service. The radio clears all + * The radio shall block the {@link ImsRegistrationImplBase.ImsRegistrationTech} + * included with this and search for other available RATs in the background. + * If no other RAT is available that meets the carrier requirements, the + * radio may remain on the blocked RAT for internet service. The radio clears all * RATs marked as unavailable if the IMS service is registered to the carrier network. * @hide */ @@ -133,7 +134,7 @@ public interface RegistrationManager { */ @SystemApi @FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION) - int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4; + int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4; /**@hide*/ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.java b/telephony/java/android/telephony/satellite/AntennaPosition.java index 8842886d3a1c..d6440fc7a119 100644 --- a/telephony/java/android/telephony/satellite/AntennaPosition.java +++ b/telephony/java/android/telephony/satellite/AntennaPosition.java @@ -35,10 +35,10 @@ import java.util.Objects; @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public final class AntennaPosition implements Parcelable { /** Antenna direction used for satellite communication. */ - @NonNull AntennaDirection mAntennaDirection; + @NonNull private AntennaDirection mAntennaDirection; /** Enum corresponding to device hold position to be used by the end user. */ - @SatelliteManager.DeviceHoldPosition int mSuggestedHoldPosition; + @SatelliteManager.DeviceHoldPosition private int mSuggestedHoldPosition; /** * @hide diff --git a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl index cd9d81e1ee9b..9ff73e2c2f12 100644 --- a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl +++ b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl @@ -20,7 +20,7 @@ package android.telephony.satellite; * Interface for satellite state change callback. * @hide */ -oneway interface ISatelliteStateCallback { +oneway interface ISatelliteModemStateCallback { /** * Indicates that the satellite modem state has changed. * diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java index 022a856e48dd..9440b65a61aa 100644 --- a/telephony/java/android/telephony/satellite/PointingInfo.java +++ b/telephony/java/android/telephony/satellite/PointingInfo.java @@ -17,6 +17,7 @@ package android.telephony.satellite; import android.annotation.FlaggedApi; +import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; @@ -108,11 +109,19 @@ public final class PointingInfo implements Parcelable { return sb.toString(); } + /** + * Returns the azimuth of the satellite, in degrees. + */ + @FloatRange(from = -180, to = 180) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public float getSatelliteAzimuthDegrees() { return mSatelliteAzimuthDegrees; } + /** + * Returns the elevation of the satellite, in degrees. + */ + @FloatRange(from = -90, to = 90) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public float getSatelliteElevationDegrees() { return mSatelliteElevationDegrees; diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java index b5763c38e69c..8e79ca5cf3d3 100644 --- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java @@ -22,10 +22,15 @@ import android.annotation.SystemApi; import com.android.internal.telephony.flags.Flags; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** * A callback class for listening to satellite datagrams. + * {@link SatelliteDatagramCallback} is registered to telephony when an app which invokes + * {@link SatelliteManager#registerForSatelliteDatagram(Executor, SatelliteDatagramCallback)}, + * and {@link #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)} will be invoked + * when a new datagram is received from satellite. * * @hide */ diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index e09bd201f93e..2a697033c132 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -75,8 +75,9 @@ public final class SatelliteManager { private static final ConcurrentHashMap<SatelliteProvisionStateCallback, ISatelliteProvisionStateCallback> sSatelliteProvisionStateCallbackMap = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap<SatelliteStateCallback, ISatelliteStateCallback> - sSatelliteStateCallbackMap = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap<SatelliteModemStateCallback, + ISatelliteModemStateCallback> + sSatelliteModemStateCallbackMap = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<SatelliteTransmissionUpdateCallback, ISatelliteTransmissionUpdateCallback> sSatelliteTransmissionUpdateCallbackMap = new ConcurrentHashMap<>(); @@ -624,6 +625,11 @@ public final class SatelliteManager { /** * Request to get whether the satellite service is supported on the device. * + * <p> + * Note: This API only checks whether the device supports the satellite feature. The result will + * not be affected by whether the device is provisioned. + * </p> + * * @param executor The executor on which the callback will be called. * @param callback The callback object to which the result will be delivered. * If the request is successful, {@link OutcomeReceiver#onResult(Object)} @@ -1301,21 +1307,22 @@ public final class SatelliteManager { @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @SatelliteResult public int registerForSatelliteModemStateChanged( @NonNull @CallbackExecutor Executor executor, - @NonNull SatelliteStateCallback callback) { + @NonNull SatelliteModemStateCallback callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { ITelephony telephony = getITelephony(); if (telephony != null) { - ISatelliteStateCallback internalCallback = new ISatelliteStateCallback.Stub() { + ISatelliteModemStateCallback internalCallback = + new ISatelliteModemStateCallback.Stub() { @Override public void onSatelliteModemStateChanged(int state) { executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onSatelliteModemStateChanged(state))); } }; - sSatelliteStateCallbackMap.put(callback, internalCallback); + sSatelliteModemStateCallbackMap.put(callback, internalCallback); return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback); } else { throw new IllegalStateException("telephony service is null."); @@ -1332,16 +1339,18 @@ public final class SatelliteManager { * If callback was not registered before, the request will be ignored. * * @param callback The callback that was passed to - * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteStateCallback)}. + * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteModemStateCallback)}. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) { + public void unregisterForSatelliteModemStateChanged( + @NonNull SatelliteModemStateCallback callback) { Objects.requireNonNull(callback); - ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback); + ISatelliteModemStateCallback internalCallback = sSatelliteModemStateCallbackMap.remove( + callback); try { ITelephony telephony = getITelephony(); diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java index bfe6e101ffb4..8d33c88136a6 100644 --- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java @@ -28,7 +28,7 @@ import com.android.internal.telephony.flags.Flags; */ @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) -public interface SatelliteStateCallback { +public interface SatelliteModemStateCallback { /** * Called when satellite modem state changes. * @param state The new satellite modem state. diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 9b5ee0cd82f3..3ea86c7bd686 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -72,7 +72,7 @@ import android.telephony.satellite.ISatelliteCapabilitiesCallback; import android.telephony.satellite.ISatelliteDatagramCallback; import android.telephony.satellite.ISatelliteTransmissionUpdateCallback; import android.telephony.satellite.ISatelliteProvisionStateCallback; -import android.telephony.satellite.ISatelliteStateCallback; +import android.telephony.satellite.ISatelliteModemStateCallback; import android.telephony.satellite.NtnSignalStrength; import android.telephony.satellite.SatelliteCapabilities; import android.telephony.satellite.SatelliteDatagram; @@ -2896,7 +2896,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback); + int registerForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback); /** * Unregisters for modem state changed from satellite modem. @@ -2907,7 +2907,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback); + void unregisterForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback); /** * Register to receive incoming datagrams over satellite. @@ -3230,4 +3230,32 @@ interface ITelephony { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)") boolean isCellularIdentifierDisclosureNotificationsEnabled(); + + /** + * Enables or disables notifications sent when cellular null cipher or integrity algorithms + * are in use by the cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.MODIFY_PHONE_STATE)") + void setEnableNullCipherNotifications(boolean enable); + + /** + * Get whether notifications are enabled for null cipher or integrity algorithms in use by the + * cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)") + boolean isNullCipherNotificationsEnabled(); } diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java index 80c1e5be3a32..217659ee4345 100644 --- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java +++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java @@ -43,9 +43,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; /** @@ -178,8 +176,14 @@ public class GraphicsActivity extends Activity { this.deviceFrameRate = deviceFrameRate; } + FrameRateTimeoutException(List<Float> expectedFrameRates, float deviceFrameRate) { + this.expectedFrameRates = expectedFrameRates; + this.deviceFrameRate = deviceFrameRate; + } + public float expectedFrameRate; public float deviceFrameRate; + public List<Float> expectedFrameRates; } public enum Api { @@ -502,31 +506,37 @@ public class GraphicsActivity extends Activity { } private void waitForStableFrameRate(TestSurface... surfaces) throws InterruptedException { - verifyCompatibleAndStableFrameRate(0, surfaces); + verifyFrameRates(List.of(), surfaces); } private void verifyExactAndStableFrameRate( float expectedFrameRate, TestSurface... surfaces) throws InterruptedException { - verifyFrameRate(expectedFrameRate, false, surfaces); + verifyFrameRate(List.of(expectedFrameRate), false, surfaces); } private void verifyCompatibleAndStableFrameRate( float expectedFrameRate, TestSurface... surfaces) throws InterruptedException { - verifyFrameRate(expectedFrameRate, true, surfaces); + verifyFrameRate(List.of(expectedFrameRate), true, surfaces); + } + + /** Verify stable frame rate at one of the expectedFrameRates. */ + private void verifyFrameRates(List<Float> expectedFrameRates, TestSurface... surfaces) + throws InterruptedException { + verifyFrameRate(expectedFrameRates, true, surfaces); } - // Set expectedFrameRate to 0.0 to verify only stable frame rate. - private void verifyFrameRate( - float expectedFrameRate, boolean multiplesAllowed, + // Set expectedFrameRates to empty to verify only stable frame rate. + private void verifyFrameRate(List<Float> expectedFrameRates, boolean multiplesAllowed, TestSurface... surfaces) throws InterruptedException { Log.i(TAG, "Verifying compatible and stable frame rate"); long nowNanos = System.nanoTime(); long gracePeriodEndTimeNanos = nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L; while (true) { - if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0 + if (expectedFrameRates.size() == 1) { + float expectedFrameRate = expectedFrameRates.get(0); // Wait until we switch to a compatible frame rate. Log.i(TAG, String.format( @@ -557,11 +567,25 @@ public class GraphicsActivity extends Activity { while (endTimeNanos > nowNanos) { int numModeChangedEvents = mModeChangedEvents.size(); if (waitForEvents(endTimeNanos, surfaces)) { - Log.i(TAG, - String.format("Stable frame rate %.2f verified", - multiplesAllowed ? mDisplayModeRefreshRate - : mDisplayRefreshRate)); - return; + // Verify any expected frame rate since there are multiple that will suffice. + // Mainly to account for running tests on real devices, where other non-test + // layers may affect the outcome. + if (expectedFrameRates.size() > 1) { + for (float expectedFrameRate : expectedFrameRates) { + if (isFrameRateMultiple(mDisplayModeRefreshRate, expectedFrameRate)) { + return; + } + } + // The frame rate is stable but it is not one of the expected frame rates. + throw new FrameRateTimeoutException( + expectedFrameRates, mDisplayModeRefreshRate); + } else { + Log.i(TAG, + String.format("Stable frame rate %.2f verified", + multiplesAllowed ? mDisplayModeRefreshRate + : mDisplayRefreshRate)); + return; + } } nowNanos = System.nanoTime(); if (mModeChangedEvents.size() > numModeChangedEvents) { @@ -623,12 +647,28 @@ public class GraphicsActivity extends Activity { // caused the timeout failure. Wait for a bit to see if we get notified // of a precondition violation, and if so, retry the test. Otherwise // fail. - assertTrue(String.format( - "Timed out waiting for a stable and compatible frame" - + " rate. expected=%.2f received=%.2f." - + " Stack trace: " + stackTrace, - exc.expectedFrameRate, exc.deviceFrameRate), - waitForPreconditionViolation()); + if (exc.expectedFrameRates.isEmpty()) { + assertTrue( + String.format( + "Timed out waiting for a stable and compatible" + + " frame rate." + + " expected=%.2f received=%.2f." + + " Stack trace: " + stackTrace, + exc.expectedFrameRate, exc.deviceFrameRate), + waitForPreconditionViolation()); + } else { + assertTrue( + String.format( + "Timed out waiting for a stable and compatible" + + " frame rate." + + " expected={%.2f} received=%.2f." + + " Stack trace: " + stackTrace, + exc.expectedFrameRates.stream() + .map(Object::toString) + .collect(Collectors.joining(", ")), + exc.deviceFrameRate), + waitForPreconditionViolation()); + } } if (!testPassed) { @@ -679,10 +719,15 @@ public class GraphicsActivity extends Activity { "**** Running testSurfaceControlFrameRateCompatibility with compatibility " + compatibility); - float expectedFrameRate = getExpectedFrameRateForCompatibility(compatibility); + List<Float> expectedFrameRates = getExpectedFrameRateForCompatibility(compatibility); + Log.i(TAG, + "Expected frame rates: " + + expectedFrameRates.stream() + .map(Object::toString) + .collect(Collectors.joining(", "))); int initialNumEvents = mModeChangedEvents.size(); surface.setFrameRate(30.f, compatibility); - verifyExactAndStableFrameRate(expectedFrameRate, surface); + verifyFrameRates(expectedFrameRates, surface); verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size()); }); } @@ -699,10 +744,10 @@ public class GraphicsActivity extends Activity { runOneSurfaceTest((TestSurface surface) -> { Log.i(TAG, "**** Running testSurfaceControlFrameRateCategory for category " + category); - float expectedFrameRate = getExpectedFrameRateForCategory(category); + List<Float> expectedFrameRates = getExpectedFrameRateForCategory(category); int initialNumEvents = mModeChangedEvents.size(); surface.setFrameRateCategory(category); - verifyCompatibleAndStableFrameRate(expectedFrameRate, surface); + verifyFrameRates(expectedFrameRates, surface); verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size()); }); } @@ -771,41 +816,41 @@ public class GraphicsActivity extends Activity { "frame rate strategy=" + parentStrategy); } - private float getExpectedFrameRateForCompatibility(int compatibility) { + private List<Float> getExpectedFrameRateForCompatibility(int compatibility) { assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED for compatibility " + compatibility, compatibility == Surface.FRAME_RATE_COMPATIBILITY_GTE); Display display = getDisplay(); - Optional<Float> expectedFrameRate = getRefreshRates(display.getMode(), display) - .stream() - .filter(rate -> rate >= 30.f) - .min(Comparator.naturalOrder()); + List<Float> expectedFrameRates = getRefreshRates(display.getMode(), display) + .stream() + .filter(rate -> rate >= 30.f) + .collect(Collectors.toList()); assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED because no refresh rate " + "is >= 30", - expectedFrameRate.isPresent()); - return expectedFrameRate.get(); + !expectedFrameRates.isEmpty()); + return expectedFrameRates; } - private float getExpectedFrameRateForCategory(int category) { + private List<Float> getExpectedFrameRateForCategory(int category) { Display display = getDisplay(); List<Float> frameRates = getRefreshRates(display.getMode(), display); if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT) { // Max due to default vote and no other frame rate specifications. - return Collections.max(frameRates); + return List.of(Collections.max(frameRates)); } else if (category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) { - return Collections.min(frameRates); + return frameRates; } FpsRange categoryRange = convertCategory(category); - Optional<Float> expectedFrameRate = frameRates.stream() - .filter(fps -> categoryRange.includes(fps)) - .min(Comparator.naturalOrder()); + List<Float> expectedFrameRates = frameRates.stream() + .filter(fps -> categoryRange.includes(fps)) + .collect(Collectors.toList()); assumeTrue("**** testSurfaceControlFrameRateCategory SKIPPED for category " + category, - expectedFrameRate.isPresent()); - return expectedFrameRate.get(); + !expectedFrameRates.isEmpty()); + return expectedFrameRates; } private FpsRange convertCategory(int category) { diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java index 29cbf01dc6da..e3988cd20199 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java @@ -24,8 +24,8 @@ import android.os.Bundle; import android.view.View; import android.widget.ToggleButton; +import androidx.window.embedding.ActivityEmbeddingController; import androidx.window.embedding.SplitAttributes; -import androidx.window.embedding.SplitAttributesCalculatorParams; import androidx.window.embedding.SplitController; /** @@ -66,7 +66,9 @@ public class ActivityEmbeddingSecondaryActivity extends Activity { @Override public void onClick(View v) { // This triggers a recalcuation of splitatributes. - mSplitController.invalidateTopVisibleSplitAttributes(); + ActivityEmbeddingController + .getInstance(ActivityEmbeddingSecondaryActivity.this) + .invalidateTopVisibleActivityStacks(); } }); findViewById(R.id.secondary_enter_pip_button).setOnClickListener( diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java index be479f205ff2..1b0279273dc7 100644 --- a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java +++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java @@ -25,6 +25,7 @@ import android.platform.test.flag.junit.host.HostFlagsValueProvider; import android.security.Flags; import com.android.blockdevicewriter.BlockDeviceWriter; +import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -52,7 +53,6 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { private static final String TARGET_PACKAGE = "com.android.fsverity"; private static final String BASENAME = "test.file"; - private static final String TARGET_PATH = "/data/data/" + TARGET_PACKAGE + "/files/" + BASENAME; @Rule public final CheckFlagsRule mCheckFlagsRule = @@ -63,11 +63,11 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { prepareTest(10000); ITestDevice device = getDevice(); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 0); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 8192); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 0); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 8192); BlockDeviceWriter.dropCaches(device); - verifyRead(TARGET_PATH, "0,2"); + verifyRead(getTargetFilePath(), "0,2"); } @Test @@ -75,12 +75,17 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { prepareTest(128 * 4096 + 1); ITestDevice device = getDevice(); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 4096); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 100 * 4096); - BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 128 * 4096 + 1); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 4096); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 100 * 4096); + BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 128 * 4096 + 1); BlockDeviceWriter.dropCaches(device); - verifyRead(TARGET_PATH, "1,100,128"); + verifyRead(getTargetFilePath(), "1,100,128"); + } + + private String getTargetFilePath() throws DeviceNotAvailableException { + return "/data/user/" + getDevice().getCurrentUser() + "/" + TARGET_PACKAGE + "/files/" + + BASENAME; } private void prepareTest(int fileSize) throws Exception { @@ -97,7 +102,7 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { options.setTestClassName(TARGET_PACKAGE + ".Helper"); options.setTestMethodName("verifyFileRead"); options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv); - options.addInstrumentationArg("filePath", TARGET_PATH); + options.addInstrumentationArg("filePath", getTargetFilePath()); assertThat(runDeviceTests(options)).isTrue(); } } diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index c1784f3b42e7..7b191f8388c5 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -22,7 +22,6 @@ import android.content.ContextWrapper import android.hardware.display.DisplayViewport import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal -import android.os.IInputConstants import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.provider.Settings @@ -295,14 +294,13 @@ class InputManagerServiceTests { localService.setPointerIconVisible(false, 10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - localService.setPointerAcceleration(5f, 10) - verify(native).setPointerAcceleration(eq(5f)) + localService.setMousePointerAccelerationEnabled(false, 10) + verify(native).setMousePointerAccelerationEnabled(eq(false)) service.onDisplayRemoved(10) verify(native).displayRemoved(eq(10)) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED)) - verify(native).setPointerAcceleration( - eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat())) + verify(native).setMousePointerAccelerationEnabled(true) verifyNoMoreInteractions(native) // This call should not block because the virtual mouse pointer override was never removed. @@ -318,38 +316,38 @@ class InputManagerServiceTests { localService.setPointerIconVisible(false, 10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - localService.setPointerAcceleration(5f, 10) - verify(native).setPointerAcceleration(eq(5f)) + localService.setMousePointerAccelerationEnabled(false, 10) + verify(native).setMousePointerAccelerationEnabled(eq(false)) localService.setPointerIconVisible(true, 10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED)) - localService.setPointerAcceleration(1f, 10) - verify(native).setPointerAcceleration(eq(1f)) + localService.setMousePointerAccelerationEnabled(true, 10) + verify(native).setMousePointerAccelerationEnabled(eq(true)) // Verify that setting properties on a different display is not propagated until the // pointer is moved to that display. localService.setPointerIconVisible(false, 20) - localService.setPointerAcceleration(6f, 20) + localService.setMousePointerAccelerationEnabled(false, 20) verifyNoMoreInteractions(native) clearInvocations(native) setVirtualMousePointerDisplayIdAndVerify(20) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - verify(native).setPointerAcceleration(eq(6f)) + verify(native).setMousePointerAccelerationEnabled(eq(false)) } @Test fun setAdditionalInputPropertiesBeforeOverride() { localService.setPointerIconVisible(false, 10) - localService.setPointerAcceleration(5f, 10) + localService.setMousePointerAccelerationEnabled(false, 10) verifyNoMoreInteractions(native) setVirtualMousePointerDisplayIdAndVerify(10) verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - verify(native).setPointerAcceleration(eq(5f)) + verify(native).setMousePointerAccelerationEnabled(eq(false)) } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java index 692c8a8f0898..49665f7a3304 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -27,15 +27,18 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY; import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR; +import static com.android.server.vcn.VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS; import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -55,6 +58,7 @@ import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; +import android.net.vcn.VcnManager; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.os.ParcelUuid; @@ -81,6 +85,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** Tests for TelephonySubscriptionTracker */ @RunWith(AndroidJUnit4.class) @@ -352,4 +357,71 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { any(Executor.class), any(ConnectivityDiagnosticsCallback.class)); } + + private void verifyGetSafeModeTimeoutMs( + boolean isInTestMode, + boolean isConfigTimeoutSupported, + PersistableBundleWrapper carrierConfig, + long expectedTimeoutMs) + throws Exception { + doReturn(isInTestMode).when(mVcnContext).isInTestMode(); + doReturn(isConfigTimeoutSupported).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled(); + + final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class); + doReturn(carrierConfig).when(snapshot).getCarrierConfigForSubGrp(TEST_SUB_GRP); + + final long result = + VcnGatewayConnection.getSafeModeTimeoutMs(mVcnContext, snapshot, TEST_SUB_GRP); + + assertEquals(expectedTimeoutMs, result); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutUnsupported() throws Exception { + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + false /* isConfigTimeoutSupported */, + null /* carrierConfig */, + TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutSupported() throws Exception { + final int carrierConfigTimeoutSeconds = 20; + final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class); + doReturn(carrierConfigTimeoutSeconds) + .when(carrierConfig) + .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt()); + + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + true /* isConfigTimeoutSupported */, + carrierConfig, + TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutSupported_carrierConfigNull() + throws Exception { + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + true /* isConfigTimeoutSupported */, + null /* carrierConfig */, + TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutOverrideTestModeDefault() throws Exception { + final int carrierConfigTimeoutSeconds = 20; + final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class); + doReturn(carrierConfigTimeoutSeconds) + .when(carrierConfig) + .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt()); + + verifyGetSafeModeTimeoutMs( + true /* isInTestMode */, + true /* isConfigTimeoutSupported */, + carrierConfig, + TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds)); + } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index edced87427c8..4c7b25aaa7c3 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -67,6 +67,8 @@ import com.android.server.IpSecService; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; +import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; +import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock; import com.android.server.vcn.routeselection.UnderlyingNetworkController; import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; @@ -118,13 +120,7 @@ public class VcnGatewayConnectionTestBase { NetworkCapabilities networkCapabilities, LinkProperties linkProperties, boolean isBlocked) { - return new UnderlyingNetworkRecord( - network, - networkCapabilities, - linkProperties, - isBlocked, - false /* isSelected */, - 0 /* priorityClass */); + return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked); } protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4"; @@ -226,6 +222,7 @@ public class VcnGatewayConnectionTestBase { doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags(); + doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled(); doReturn(mUnderlyingNetworkController) .when(mDeps) diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java new file mode 100644 index 000000000000..9daba6a79a27 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java @@ -0,0 +1,419 @@ +/* + * 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.server.vcn.routeselection; + +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY; +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY; + +import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.net.IpSecTransformState; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.concurrent.TimeUnit; + +public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { + private static final String TAG = IpSecPacketLossDetectorTest.class.getSimpleName(); + + private static final int REPLAY_BITMAP_LEN_BYTE = 512; + private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8; + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5; + private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L); + + @Mock private IpSecTransformWrapper mIpSecTransform; + @Mock private NetworkMetricMonitorCallback mMetricMonitorCallback; + @Mock private PersistableBundleWrapper mCarrierConfig; + @Mock private IpSecPacketLossDetector.Dependencies mDependencies; + @Spy private PacketLossCalculator mPacketLossCalculator = new PacketLossCalculator(); + + @Captor private ArgumentCaptor<OutcomeReceiver> mTransformStateReceiverCaptor; + @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + + private IpSecPacketLossDetector mIpSecPacketLossDetector; + private IpSecTransformState mTransformStateInitial; + + @Before + public void setUp() throws Exception { + super.setUp(); + mTransformStateInitial = newTransformState(0, 0, newReplayBitmap(0)); + + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn((int) TimeUnit.MILLISECONDS.toSeconds(POLL_IPSEC_STATE_INTERVAL_MS)); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY), + anyInt())) + .thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD); + + when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator); + + mIpSecPacketLossDetector = + new IpSecPacketLossDetector( + mVcnContext, + mNetwork, + mCarrierConfig, + mMetricMonitorCallback, + mDependencies); + } + + private static IpSecTransformState newTransformState( + long rxSeqNo, long packtCount, byte[] replayBitmap) { + return new IpSecTransformState.Builder() + .setRxHighestSequenceNumber(rxSeqNo) + .setPacketCount(packtCount) + .setReplayBitmap(replayBitmap) + .build(); + } + + private static byte[] newReplayBitmap(int receivedPktCnt) { + final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT); + for (int i = 0; i < receivedPktCnt; i++) { + bitSet.set(i); + } + return Arrays.copyOf(bitSet.toByteArray(), REPLAY_BITMAP_LEN_BYTE); + } + + private void verifyStopped() { + assertFalse(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + + // No event scheduled + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + } + + @Test + public void testInitialization() throws Exception { + assertFalse(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + verifyStopped(); + } + + private OutcomeReceiver<IpSecTransformState, RuntimeException> + startMonitorAndCaptureStateReceiver() { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + // Trigger the runnable + mTestLooper.dispatchAll(); + + verify(mIpSecTransform) + .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture()); + return mTransformStateReceiverCaptor.getValue(); + } + + @Test + public void testStartMonitor() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertTrue(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + assertEquals(mIpSecTransform, mIpSecPacketLossDetector.getInboundTransformInternal()); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Verify the first polled state is stored + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Verify next poll is scheduled + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStartedMonitor_enterDozeMoze() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Mock entering doze mode + final Intent intent = mock(Intent.class); + when(intent.getAction()).thenReturn(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + when(mPowerManagerService.isDeviceIdleMode()).thenReturn(true); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); + final BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue(); + broadcastReceiver.onReceive(mContext, intent); + + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + } + + @Test + public void testStartedMonitor_updateInboundTransform() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Update the inbound transform + final IpSecTransformWrapper newTransform = mock(IpSecTransformWrapper.class); + mIpSecPacketLossDetector.setInboundTransformInternal(newTransform); + + // Verifications + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + verify(newTransform).getIpSecTransformState(any(), any()); + } + + @Test + public void testStartedMonitor_updateCarrierConfig() throws Exception { + startMonitorAndCaptureStateReceiver(); + + final int additionalPollIntervalMs = (int) TimeUnit.SECONDS.toMillis(10L); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn( + (int) + TimeUnit.MILLISECONDS.toSeconds( + POLL_IPSEC_STATE_INTERVAL_MS + additionalPollIntervalMs)); + mIpSecPacketLossDetector.setCarrierConfig(mCarrierConfig); + mTestLooper.dispatchAll(); + + // The already scheduled event is still fired with the old timeout + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + + // The next scheduled event will take 10 more seconds to fire + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(additionalPollIntervalMs); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStopMonitor() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + } + + @Test + public void testClose() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Stop the monitor + mIpSecPacketLossDetector.close(); + verifyStopped(); + verify(mIpSecTransform).close(); + } + + @Test + public void testTransformStateReceiverOnResultWhenStopped() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + + xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1))); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + @Test + public void testTransformStateReceiverOnError() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + xfrmStateReceiver.onError(new RuntimeException("Test")); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + private void checkHandleLossRate( + int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) + throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + doReturn(mockPacketLossRate) + .when(mPacketLossCalculator) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Mock receiving two states with mTransformStateInitial and an arbitrary transformNew + final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1)); + xfrmStateReceiver.onResult(mTransformStateInitial); + xfrmStateReceiver.onResult(transformNew); + + // Verifications + verify(mPacketLossCalculator) + .getPacketLossRatePercentage( + eq(mTransformStateInitial), eq(transformNew), anyString()); + + if (isLastStateExpectedToUpdate) { + assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState()); + } else { + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + } + + if (isCallbackExpected) { + verify(mMetricMonitorCallback).onValidationResultReceived(); + } else { + verify(mMetricMonitorCallback, never()).onValidationResultReceived(); + } + } + + @Test + public void testHandleLossRate_validationPass() throws Exception { + checkHandleLossRate( + 2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_validationFail() throws Exception { + checkHandleLossRate( + 22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_resultUnavalaible() throws Exception { + checkHandleLossRate( + PACKET_LOSS_UNAVALAIBLE, + false /* isLastStateExpectedToUpdate */, + false /* isCallbackExpected */); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate) + throws Exception { + assertEquals( + expectedLossRate, + mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG)); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, + int rxSeqNo, + int packetCount, + int packetInWin, + int expectedDataLossRate) + throws Exception { + final IpSecTransformState newState = + newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); + checkGetPacketLossRate(oldState, newState, expectedDataLossRate); + } + + @Test + public void testGetPacketLossRate_replayWindowUnchanged() throws Exception { + checkGetPacketLossRate( + mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE); + checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE); + } + + @Test + public void testGetPacketLossRate_againstInitialState() throws Exception { + checkGetPacketLossRate(mTransformStateInitial, 7000, 7001, 4096, 0); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4096, 15); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4000, 14); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 5000, 5001, 4096, 0); + checkGetPacketLossRate(oldState, 5000, 4000, 4096, 29); + checkGetPacketLossRate(oldState, 5000, 4000, 4000, 27); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 7000, 7001, 4096, 0); + checkGetPacketLossRate(oldState, 7000, 5000, 4096, 37); + checkGetPacketLossRate(oldState, 7000, 5000, 3000, 21); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 12000, 8096, 4096, 0); + checkGetPacketLossRate(oldState, 12000, 7000, 4096, 36); + checkGetPacketLossRate(oldState, 12000, 7000, 3000, 0); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 20000, 16096, 4096, 0); + checkGetPacketLossRate(oldState, 20000, 14000, 4096, 19); + checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java new file mode 100644 index 000000000000..355c22156a78 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -0,0 +1,144 @@ +/* + * 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.server.vcn.routeselection; + +import static com.android.server.vcn.VcnTestUtils.setupSystemService; +import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.TelephonyNetworkSpecifier; +import android.net.vcn.FeatureFlags; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; +import android.os.ParcelUuid; +import android.os.PowerManager; +import android.os.test.TestLooper; +import android.telephony.TelephonyManager; + +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.VcnContext; +import com.android.server.vcn.VcnNetworkProvider; + +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Set; +import java.util.UUID; + +public abstract class NetworkEvaluationTestBase { + protected static final String SSID = "TestWifi"; + protected static final String SSID_OTHER = "TestWifiOther"; + protected static final String PLMN_ID = "123456"; + protected static final String PLMN_ID_OTHER = "234567"; + + protected static final int SUB_ID = 1; + protected static final int WIFI_RSSI = -60; + protected static final int WIFI_RSSI_HIGH = -50; + protected static final int WIFI_RSSI_LOW = -80; + protected static final int CARRIER_ID = 1; + protected static final int CARRIER_ID_OTHER = 2; + + protected static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024; + protected static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048; + + protected static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100; + protected static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200; + + protected static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); + + protected static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .setSignalStrength(WIFI_RSSI) + .setSsid(SSID) + .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) + .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) + .build(); + + protected static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = + new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); + protected static final NetworkCapabilities CELL_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setSubscriptionIds(Set.of(SUB_ID)) + .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) + .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) + .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) + .build(); + + protected static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); + + @Mock protected Context mContext; + @Mock protected Network mNetwork; + @Mock protected FeatureFlags mFeatureFlags; + @Mock protected com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; + @Mock protected TelephonySubscriptionSnapshot mSubscriptionSnapshot; + @Mock protected TelephonyManager mTelephonyManager; + @Mock protected IPowerManager mPowerManagerService; + + protected TestLooper mTestLooper; + protected VcnContext mVcnContext; + protected PowerManager mPowerManager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mNetwork.getNetId()).thenReturn(-1); + + mTestLooper = new TestLooper(); + mVcnContext = + spy( + new VcnContext( + mContext, + mTestLooper.getLooper(), + mock(VcnNetworkProvider.class), + false /* isInTestMode */)); + doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + + doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled(); + doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled(); + + setupSystemService( + mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); + when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); + when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); + when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); + + mPowerManager = + new PowerManager( + mContext, + mPowerManagerService, + mock(IThermalService.class), + mock(Handler.class)); + setupSystemService(mContext, mPowerManager, Context.POWER_SERVICE, PowerManager.class); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index 226604108522..d85c5150f53f 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -24,152 +24,48 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTR import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS; import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS; -import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule; -import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.content.Context; -import android.net.LinkProperties; -import android.net.Network; import android.net.NetworkCapabilities; -import android.net.TelephonyNetworkSpecifier; import android.net.vcn.VcnCellUnderlyingNetworkTemplate; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.net.vcn.VcnWifiUnderlyingNetworkTemplate; -import android.os.ParcelUuid; import android.os.PersistableBundle; -import android.os.test.TestLooper; -import android.telephony.TelephonyManager; import android.util.ArraySet; -import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.VcnContext; -import com.android.server.vcn.VcnNetworkProvider; - import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.UUID; - -public class NetworkPriorityClassifierTest { - private static final String SSID = "TestWifi"; - private static final String SSID_OTHER = "TestWifiOther"; - private static final String PLMN_ID = "123456"; - private static final String PLMN_ID_OTHER = "234567"; - - private static final int SUB_ID = 1; - private static final int WIFI_RSSI = -60; - private static final int WIFI_RSSI_HIGH = -50; - private static final int WIFI_RSSI_LOW = -80; - private static final int CARRIER_ID = 1; - private static final int CARRIER_ID_OTHER = 2; - - private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024; - private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048; - - private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100; - private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200; - - private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); - - private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES = - new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .setSignalStrength(WIFI_RSSI) - .setSsid(SSID) - .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) - .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) - .build(); - - private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = - new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); - private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES = - new NetworkCapabilities.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setSubscriptionIds(Set.of(SUB_ID)) - .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) - .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) - .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) - .build(); - - private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); - - @Mock private Network mNetwork; - @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot; - @Mock private TelephonyManager mTelephonyManager; - - private TestLooper mTestLooper; - private VcnContext mVcnContext; + +public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase { private UnderlyingNetworkRecord mWifiNetworkRecord; private UnderlyingNetworkRecord mCellNetworkRecord; @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - final Context mockContext = mock(Context.class); - mTestLooper = new TestLooper(); - mVcnContext = - spy( - new VcnContext( - mockContext, - mTestLooper.getLooper(), - mock(VcnNetworkProvider.class), - false /* isInTestMode */)); - doNothing().when(mVcnContext).ensureRunningOnLooperThread(); - - setupSystemService( - mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); - when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); - when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); - when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); - - mWifiNetworkRecord = - getTestNetworkRecord( - WIFI_NETWORK_CAPABILITIES, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); - mCellNetworkRecord = - getTestNetworkRecord( - CELL_NETWORK_CAPABILITIES, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); - } - - private UnderlyingNetworkRecord getTestNetworkRecord( - NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { - return new UnderlyingNetworkRecord( - mNetwork, - nc, - LINK_PROPERTIES, - false /* isBlocked */, - mVcnContext, - underlyingNetworkTemplates, - SUB_GROUP, - mSubscriptionSnapshot, - null /* currentlySelected */, - null /* carrierConfig */); + public void setUp() throws Exception { + super.setUp(); + + mWifiNetworkRecord = getTestNetworkRecord(WIFI_NETWORK_CAPABILITIES); + mCellNetworkRecord = getTestNetworkRecord(CELL_NETWORK_CAPABILITIES); + } + + private UnderlyingNetworkRecord getTestNetworkRecord(NetworkCapabilities nc) { + return new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */); } @Test @@ -186,14 +82,14 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - null /* currentlySelecetd */, + false /* isSelected */, null /* carrierConfig */)); } private void verifyMatchesPriorityRuleForUpstreamBandwidth( int entryUpstreamBandwidth, int exitUpstreamBandwidth, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, boolean expectMatch) { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() @@ -208,14 +104,14 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - currentlySelected, + isSelected, null /* carrierConfig */)); } private void verifyMatchesPriorityRuleForDownstreamBandwidth( int entryDownstreamBandwidth, int exitDownstreamBandwidth, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, boolean expectMatch) { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() @@ -231,7 +127,7 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - currentlySelected, + isSelected, null /* carrierConfig */)); } @@ -240,7 +136,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, - null /* currentlySelected */, + false /* isSelected */, true); } @@ -249,7 +145,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( LINK_UPSTREAM_BANDWIDTH_KBPS + 1, LINK_UPSTREAM_BANDWIDTH_KBPS + 1, - null /* currentlySelected */, + false /* isSelected */, false); } @@ -258,7 +154,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, - null /* currentlySelected */, + false /* isSelected */, true); } @@ -267,7 +163,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, - null /* currentlySelected */, + false /* isSelected */, false); } @@ -276,7 +172,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, - mWifiNetworkRecord, + true /* isSelected */, true); } @@ -285,7 +181,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( LINK_UPSTREAM_BANDWIDTH_KBPS + 1, LINK_UPSTREAM_BANDWIDTH_KBPS + 1, - mWifiNetworkRecord, + true /* isSelected */, false); } @@ -294,7 +190,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, - mWifiNetworkRecord, + true /* isSelected */, true); } @@ -303,7 +199,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, - mWifiNetworkRecord, + true /* isSelected */, false); } @@ -318,14 +214,12 @@ public class NetworkPriorityClassifierTest { TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS) .build(); - final UnderlyingNetworkRecord selectedNetworkRecord = - isSelectedNetwork ? mWifiNetworkRecord : null; assertEquals( expectMatch, checkMatchesWifiPriorityRule( wifiNetworkPriority, mWifiNetworkRecord, - selectedNetworkRecord, + isSelectedNetwork, carrierConfig == null ? null : new PersistableBundleWrapper(carrierConfig))); @@ -381,7 +275,7 @@ public class NetworkPriorityClassifierTest { checkMatchesWifiPriorityRule( wifiNetworkPriority, mWifiNetworkRecord, - null /* currentlySelecetd */, + false /* isSelected */, null /* carrierConfig */)); } @@ -516,7 +410,7 @@ public class NetworkPriorityClassifierTest { mCellNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - null /* currentlySelected */, + false /* isSelected */, null /* carrierConfig */)); } @@ -543,7 +437,16 @@ public class NetworkPriorityClassifierTest { @Test public void testCalculatePriorityClass() throws Exception { - assertEquals(2, mCellNetworkRecord.priorityClass); + final int priorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + mCellNetworkRecord, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + false /* isSelected */, + null /* carrierConfig */); + assertEquals(2, priorityClass); } private void checkCalculatePriorityClassFailToMatchAny( @@ -561,10 +464,19 @@ public class NetworkPriorityClassifierTest { ncBuilder.addCapability(NET_CAPABILITY_INTERNET); } - final UnderlyingNetworkRecord nonDunNetworkRecord = - getTestNetworkRecord(ncBuilder.build(), templatesRequireDun); + final UnderlyingNetworkRecord nonDunNetworkRecord = getTestNetworkRecord(ncBuilder.build()); + + final int priorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + nonDunNetworkRecord, + templatesRequireDun, + SUB_GROUP, + mSubscriptionSnapshot, + false /* isSelected */, + null /* carrierConfig */); - assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass); + assertEquals(expectedPriorityClass, priorityClass); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java index 2941fdea20bb..992f10275739 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java @@ -29,13 +29,12 @@ import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WI import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -67,6 +66,7 @@ import android.util.ArraySet; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; +import com.android.server.vcn.routeselection.UnderlyingNetworkController.Dependencies; import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener; @@ -77,6 +77,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import java.util.ArrayList; import java.util.Arrays; @@ -154,10 +155,13 @@ public class UnderlyingNetworkControllerTest { @Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb; @Mock private Network mNetwork; + @Spy private Dependencies mDependencies = new Dependencies(); + @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor; private TestLooper mTestLooper; private VcnContext mVcnContext; + private UnderlyingNetworkEvaluator mNetworkEvaluator; private UnderlyingNetworkController mUnderlyingNetworkController; @Before @@ -189,13 +193,28 @@ public class UnderlyingNetworkControllerTest { when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS); + mNetworkEvaluator = + spy( + new UnderlyingNetworkEvaluator( + mVcnContext, + mNetwork, + VcnGatewayConnectionConfigTest.buildTestConfig() + .getVcnUnderlyingNetworkPriorities(), + SUB_GROUP, + mSubscriptionSnapshot, + null)); + doReturn(mNetworkEvaluator) + .when(mDependencies) + .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any()); + mUnderlyingNetworkController = new UnderlyingNetworkController( mVcnContext, VcnGatewayConnectionConfigTest.buildTestConfig(), SUB_GROUP, mSubscriptionSnapshot, - mNetworkControllerCb); + mNetworkControllerCb, + mDependencies); } private void resetVcnContext() { @@ -489,13 +508,7 @@ public class UnderlyingNetworkControllerTest { NetworkCapabilities networkCapabilities, LinkProperties linkProperties, boolean isBlocked) { - return new UnderlyingNetworkRecord( - network, - networkCapabilities, - linkProperties, - isBlocked, - false /* isSelected */, - 0 /* priorityClass */); + return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked); } @Test @@ -515,24 +528,12 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkRecord recordC = new UnderlyingNetworkRecord( mNetwork, - INITIAL_NETWORK_CAPABILITIES, - INITIAL_LINK_PROPERTIES, - false /* isBlocked */, - true /* isSelected */, - -1 /* priorityClass */); - UnderlyingNetworkRecord recordD = - getTestNetworkRecord( - mNetwork, UPDATED_NETWORK_CAPABILITIES, UPDATED_LINK_PROPERTIES, false /* isBlocked */); assertEquals(recordA, recordB); - assertEquals(recordA, recordC); - assertNotEquals(recordA, recordD); - - assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB)); - assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC)); + assertNotEquals(recordA, recordC); } @Test @@ -540,6 +541,19 @@ public class UnderlyingNetworkControllerTest { verifyRegistrationOnAvailableAndGetCallback(); } + @Test + public void testUpdateSubscriptionSnapshotAndCarrierConfig() { + verifyRegistrationOnAvailableAndGetCallback(); + + TelephonySubscriptionSnapshot subscriptionUpdate = + mock(TelephonySubscriptionSnapshot.class); + when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS); + + mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate); + + verify(mNetworkEvaluator).reevaluate(any(), any(), any(), any()); + } + private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() { return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES); } @@ -583,6 +597,7 @@ public class UnderlyingNetworkControllerTest { INITIAL_LINK_PROPERTIES, false /* isBlocked */); verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); + verify(mNetworkEvaluator).setIsSelected(eq(true), any(), any(), any(), any()); return cb; } @@ -713,7 +728,8 @@ public class UnderlyingNetworkControllerTest { VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates), SUB_GROUP, mSubscriptionSnapshot, - mNetworkControllerCb); + mNetworkControllerCb, + mDependencies); verify(cm) .registerNetworkCallback( @@ -724,30 +740,43 @@ public class UnderlyingNetworkControllerTest { return mUnderlyingNetworkListenerCaptor.getValue(); } - private UnderlyingNetworkRecord bringupNetworkAndGetRecord( + private UnderlyingNetworkEvaluator bringupNetworkAndGetEvaluator( UnderlyingNetworkListener cb, NetworkCapabilities requestNetworkCaps, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - UnderlyingNetworkRecord currentlySelected) { + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { final Network network = mock(Network.class); final NetworkCapabilities responseNetworkCaps = buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS); + final UnderlyingNetworkEvaluator evaluator = + spy( + new UnderlyingNetworkEvaluator( + mVcnContext, + network, + underlyingNetworkTemplates, + SUB_GROUP, + mSubscriptionSnapshot, + null)); + doReturn(evaluator) + .when(mDependencies) + .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any()); cb.onAvailable(network); cb.onCapabilitiesChanged(network, responseNetworkCaps); cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES); cb.onBlockedStatusChanged(network, false /* isFalse */); - return new UnderlyingNetworkRecord( - network, - responseNetworkCaps, - INITIAL_LINK_PROPERTIES, - false /* isBlocked */, - mVcnContext, - underlyingNetworkTemplates, - SUB_GROUP, - mSubscriptionSnapshot, - currentlySelected, - null /* carrierConfig */); + + return evaluator; + } + + private void verifySelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) { + verifyOnSelectedUnderlyingNetworkChanged(expectedEvaluator.getNetworkRecord()); + verify(expectedEvaluator).setIsSelected(eq(true), any(), any(), any(), any()); + } + + private void verifyNeverSelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) { + verify(mNetworkControllerCb, never()) + .onSelectedUnderlyingNetworkChanged(eq(expectedEvaluator.getNetworkRecord())); + verify(expectedEvaluator, never()).setIsSelected(eq(true), any(), any(), any(), any()); } @Test @@ -759,19 +788,15 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up CBS network - final UnderlyingNetworkRecord cbsNetworkRecord = - bringupNetworkAndGetRecord( - cb, - CBS_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + final UnderlyingNetworkEvaluator cbsNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(cbsNetworkEvaluator); // Bring up DUN network - final UnderlyingNetworkRecord dunNetworkRecord = - bringupNetworkAndGetRecord( - cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + final UnderlyingNetworkEvaluator dunNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(dunNetworkEvaluator); + verify(cbsNetworkEvaluator).setIsSelected(eq(false), any(), any(), any(), any()); } @Test @@ -783,20 +808,14 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up DUN network - final UnderlyingNetworkRecord dunNetworkRecord = - bringupNetworkAndGetRecord( - cb, - DUN_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + final UnderlyingNetworkEvaluator dunNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(dunNetworkEvaluator); // Bring up CBS network - final UnderlyingNetworkRecord cbsNetworkRecord = - bringupNetworkAndGetRecord( - cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord); - verify(mNetworkControllerCb, never()) - .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + final UnderlyingNetworkEvaluator cbsNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifyNeverSelectNetwork(cbsNetworkEvaluator); } @Test @@ -808,13 +827,9 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up an Internet network without DUN capability - final UnderlyingNetworkRecord networkRecord = - bringupNetworkAndGetRecord( - cb, - INITIAL_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord)); + final UnderlyingNetworkEvaluator evaluator = + bringupNetworkAndGetEvaluator(cb, INITIAL_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(evaluator); } @Test @@ -825,10 +840,8 @@ public class UnderlyingNetworkControllerTest { new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build()); UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); - bringupNetworkAndGetRecord( - cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */); - - verify(mNetworkControllerCb, never()) - .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class)); + final UnderlyingNetworkEvaluator evaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifyNeverSelectNetwork(evaluator); } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java new file mode 100644 index 000000000000..985e70c9771e --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java @@ -0,0 +1,101 @@ +/* + * 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.server.vcn.routeselection; + +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.net.vcn.VcnGatewayConnectionConfig; +import android.os.PersistableBundle; + +import org.junit.Before; +import org.junit.Test; + +public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase { + private PersistableBundleWrapper mCarrierConfig; + + @Before + public void setUp() throws Exception { + super.setUp(); + mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle()); + } + + private UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator() { + return new UnderlyingNetworkEvaluator( + mVcnContext, + mNetwork, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + } + + @Test + public void testInitializedEvaluator() throws Exception { + final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator(); + + assertFalse(evaluator.isValid()); + assertEquals(mNetwork, evaluator.getNetwork()); + assertEquals(PRIORITY_INVALID, evaluator.getPriorityClass()); + + try { + evaluator.getNetworkRecord(); + fail("Expected to fail because evaluator is not valid"); + } catch (Exception expected) { + } + } + + @Test + public void testValidEvaluator() { + final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator(); + evaluator.setNetworkCapabilities( + CELL_NETWORK_CAPABILITIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setLinkProperties( + LINK_PROPERTIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setIsBlocked( + false /* isBlocked */, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + + final UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + CELL_NETWORK_CAPABILITIES, + LINK_PROPERTIES, + false /* isBlocked */); + + assertTrue(evaluator.isValid()); + assertEquals(mNetwork, evaluator.getNetwork()); + assertEquals(2, evaluator.getPriorityClass()); + assertEquals(expectedRecord, evaluator.getNetworkRecord()); + } +} |