diff options
64 files changed, 883 insertions, 486 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 646e05c50a76..9961c4fdf3f7 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -1791,7 +1791,10 @@ public class JobInfo implements Parcelable { * <ol> * <li>Run as soon as possible</li> * <li>Be less restricted during Doze and battery saver</li> - * <li>Bypass Doze, app standby, and battery saver network restrictions</li> + * <li> + * Bypass Doze, app standby, and battery saver network restrictions (if the job + * has a {@link #setRequiredNetwork(NetworkRequest) connectivity constraint}) + * </li> * <li>Be less likely to be killed than regular jobs</li> * <li>Be subject to background location throttling</li> * <li>Be exempt from delay to optimize job execution</li> 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 23b36e20b174..bff43534ce05 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -3859,10 +3859,16 @@ public class JobSchedulerService extends com.android.server.SystemService // Only let the app use the higher runtime if it hasn't repeatedly timed out. final String timeoutTag = job.shouldTreatAsExpeditedJob() ? QUOTA_TRACKER_TIMEOUT_EJ_TAG : QUOTA_TRACKER_TIMEOUT_REG_TAG; + // Developers are informed that expedited jobs can be stopped earlier than regular jobs + // and so shouldn't use them for long pieces of work. There's little reason to let + // them run longer than the normal 10 minutes. + final long normalUpperLimitMs = job.shouldTreatAsExpeditedJob() + ? mConstants.RUNTIME_MIN_GUARANTEE_MS + : mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; final long upperLimitMs = mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(), job.getTimeoutBlamePackageName(), timeoutTag) - ? mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS + ? normalUpperLimitMs : mConstants.RUNTIME_MIN_GUARANTEE_MS; return Math.min(upperLimitMs, mConstants.USE_TARE_POLICY diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 2d49cfb3b171..721a8bdce57a 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -427,34 +427,30 @@ public final class JobServiceContext implements ServiceConnection { boolean binding = false; boolean startedWithForegroundFlag = false; try { - final Context.BindServiceFlags bindFlags; + long bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_APP_COMPONENT_USAGE; if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) { // If the user has bg restricted the app, don't give the job FG privileges // such as bypassing data saver or getting the higher foreground proc state. // If we've gotten to this point, the app is most likely in the foreground, // so the job will run just fine while the user keeps the app in the foreground. - bindFlags = Context.BindServiceFlags.of( - Context.BIND_AUTO_CREATE - | Context.BIND_ALMOST_PERCEPTIBLE - | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS - | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS - | Context.BIND_NOT_APP_COMPONENT_USAGE); + bindFlags |= Context.BIND_ALMOST_PERCEPTIBLE; + if (job.hasConnectivityConstraint()) { + // Only add network restriction bypass flags if the job requires network. + bindFlags |= Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS + | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS; + } startedWithForegroundFlag = true; } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { - bindFlags = Context.BindServiceFlags.of( - Context.BIND_AUTO_CREATE - | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALMOST_PERCEPTIBLE - | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS - | Context.BIND_NOT_APP_COMPONENT_USAGE); + bindFlags |= Context.BIND_NOT_FOREGROUND | Context.BIND_ALMOST_PERCEPTIBLE; + if (job.hasConnectivityConstraint()) { + // Only add network restriction bypass flags if the job requires network. + bindFlags |= Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS; + } } else { - bindFlags = Context.BindServiceFlags.of( - Context.BIND_AUTO_CREATE - | Context.BIND_NOT_FOREGROUND - | Context.BIND_NOT_PERCEPTIBLE - | Context.BIND_NOT_APP_COMPONENT_USAGE); + bindFlags |= Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE; } - binding = mContext.bindServiceAsUser(intent, this, bindFlags, + binding = mContext.bindServiceAsUser(intent, this, + Context.BindServiceFlags.of(bindFlags), UserHandle.of(job.getUserId())); } catch (SecurityException e) { // Some permission policy, for example INTERACT_ACROSS_USERS and diff --git a/core/api/current.txt b/core/api/current.txt index 1b0311d19a8d..013010be95be 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -18633,6 +18633,7 @@ package android.hardware.biometrics { method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential(); method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement(); method @Nullable public javax.crypto.Mac getMac(); + method @FlaggedApi("android.hardware.biometrics.get_op_id_crypto_object") public long getOpId(); method @Nullable public android.security.identity.PresentationSession getPresentationSession(); method @Nullable public java.security.Signature getSignature(); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 814be2f8d006..346d62bf8a30 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4090,7 +4090,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_MODULE = 4194304; // 0x400000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000 - field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000 + field @Deprecated public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000 field public static final int PROTECTION_FLAG_ROLE = 67108864; // 0x4000000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000 diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index cdda12eebdc4..012b6c49f4c5 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -273,6 +273,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * to the <code>retailDemo</code> value of * {@link android.R.attr#protectionLevel}. * + * @deprecated This flag has been replaced by the retail demo role and is a no-op since Android + * V. + * * @hide */ @SystemApi diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 7a43286692c4..97bbfbbe4908 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; +import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; @@ -851,6 +852,14 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan public @Nullable KeyAgreement getKeyAgreement() { return super.getKeyAgreement(); } + + /** + * Get the operation handle associated with this object or 0 if none. + */ + @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT) + public long getOpId() { + return super.getOpId(); + } } /** diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java index 39fbe83b6abb..8d3ea3f914ec 100644 --- a/core/java/android/hardware/biometrics/CryptoObject.java +++ b/core/java/android/hardware/biometrics/CryptoObject.java @@ -154,7 +154,7 @@ public class CryptoObject { * @hide * @return the opId associated with this object or 0 if none */ - public final long getOpId() { + public long getOpId() { if (mCrypto == null) { return 0; } else if (mCrypto instanceof IdentityCredential) { diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig index 979f5705378c..56ec7632fa20 100644 --- a/core/java/android/hardware/biometrics/flags.aconfig +++ b/core/java/android/hardware/biometrics/flags.aconfig @@ -14,3 +14,10 @@ flag { bug: "282058146" } +flag { + name: "get_op_id_crypto_object" + namespace: "biometrics" + description: "Feature flag for adding a get operation id api to CryptoObject." + bug: "307601768" +} + diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 9f931b4059c8..c012ff34bfab 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -15279,6 +15279,7 @@ public final class Settings { * max_history_files (int) * max_history_buffer_kb (int) * battery_charged_delay_ms (int) + * battery_charging_enforce_level (int) * </pre> * * <p> @@ -18306,8 +18307,11 @@ public final class Settings { /** * Delay for sending ACTION_CHARGING after device is plugged in. - * This is used as an override for constants defined in BatteryStatsImpl for - * ease of experimentation. + * This is used as an override for constants defined in BatteryStatsImpl. Its purposes are: + * <ol> + * <li>Ease of experimentation</li> + * <li>Customization of different device</li> + * </ol> * * @see com.android.internal.os.BatteryStatsImpl.Constants.KEY_BATTERY_CHARGED_DELAY_MS * @hide @@ -18317,6 +18321,22 @@ public final class Settings { "battery_charging_state_update_delay"; /** + * Threshold battery level to enforce battery state as charging. That means when battery + * level is equal to or higher than this threshold, it is always considered charging, even + * if battery level lowered. + * This is used as an override for constants defined in BatteryStatsImpl. Its purposes are: + * <ol> + * <li>Ease of experimentation</li> + * <li>Customization of different device</li> + * </ol> + * + * @hide + * @see com.android.internal.os.BatteryStatsImpl.Constants.BATTERY_CHARGING_ENFORCE_LEVEL + */ + public static final String BATTERY_CHARGING_STATE_ENFORCE_LEVEL = + "battery_charging_state_enforce_level"; + + /** * A serialized string of params that will be loaded into a text classifier action model. * * @hide diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 9d88af95ade8..451a71b71e3f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -2671,7 +2671,7 @@ public final class SurfaceControl implements Parcelable { * * @param minAlpha The min alpha the {@link SurfaceControl} is required to * have to be considered inside the threshold. - * @param minFractionRendered The min fraction of the SurfaceControl that was resented + * @param minFractionRendered The min fraction of the SurfaceControl that was presented * to the user to be considered inside the threshold. * @param stabilityRequirementMs The time in milliseconds required for the * {@link SurfaceControl} to be in the threshold. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 95f14931c6ef..9f99dc94bc92 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -301,7 +301,9 @@ granted to the system companion device manager service --> <flag name="companion" value="0x800000" /> <!-- Additional flag from base permission type: this permission will be granted to the - retail demo app, as defined by the OEM. --> + retail demo app, as defined by the OEM. + This flag has been replaced by the retail demo role and is a no-op since Android V. + --> <flag name="retailDemo" value="0x1000000" /> <!-- Additional flag from base permission type: this permission will be granted to the recents app. --> diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml index 1df11369a049..5b2ffec67e93 100644 --- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml @@ -95,14 +95,6 @@ <option name="pull-pattern-keys" value="perfetto_file_path"/> <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml index 1df11369a049..9f7d9fcf1326 100644 --- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml @@ -94,15 +94,7 @@ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="pull-pattern-keys" value="perfetto_file_path"/> <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker/files"/> - <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml index 1df11369a049..882b200da3a2 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml @@ -94,15 +94,9 @@ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="pull-pattern-keys" value="perfetto_file_path"/> <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> - <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.service/files"/> + value="/data/user/0/com.android.wm.shell.flicker.pip.apps/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml index ca182fa5a266..6df65391ea61 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml @@ -57,6 +57,17 @@ <option name="test-file-name" value="WMShellFlickerTestsPipAppsCSuite.apk"/> <option name="test-file-name" value="FlickerTestApp.apk"/> </target_preparer> + + <!-- Needed for installing apk's from Play Store --> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="aapt-version" value="AAPT2"/> + <option name="throw-if-not-found" value="false"/> + <option name="install-arg" value="-d"/> + <option name="install-arg" value="-g"/> + <option name="install-arg" value="-r"/> + <option name="test-file-name" value="pstash://com.netflix.mediaclient"/> + </target_preparer> + <!-- Enable mocking GPS location by the test app --> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" @@ -94,28 +105,8 @@ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="pull-pattern-keys" value="perfetto_file_path"/> <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> - <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker.pip.apps/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> </metrics_collector> - - <!-- Needed for installing apk's from Play Store --> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> - <option name="aapt-version" value="AAPT2"/> - <option name="throw-if-not-found" value="false"/> - <option name="install-arg" value="-d"/> - <option name="install-arg" value="-g"/> - <option name="install-arg" value="-r"/> - <option name="test-file-name" value="pstash://com.netflix.mediaclient"/> - </target_preparer> </configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml index 1df11369a049..51a55e359acf 100644 --- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml @@ -94,14 +94,6 @@ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="pull-pattern-keys" value="perfetto_file_path"/> <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> - <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml index 1df11369a049..fdda5974d1f9 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml @@ -94,15 +94,7 @@ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="pull-pattern-keys" value="perfetto_file_path"/> <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> - <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> - <option name="directory-keys" - value="/data/user/0/com.android.wm.shell.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> </metrics_collector> diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index abd928486607..576ebc1579ef 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -24,6 +24,7 @@ #include <SkColor.h> #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> +#include <com_android_input_flags.h> #include <ftl/enum.h> #include <mutex> @@ -34,6 +35,8 @@ #define INDENT2 " " #define INDENT3 " " +namespace input_flags = com::android::input::flags; + namespace android { namespace { @@ -63,10 +66,20 @@ void PointerController::DisplayInfoListener::onPointerControllerDestroyed() { std::shared_ptr<PointerController> PointerController::create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, - SpriteController& spriteController, bool enabled) { + SpriteController& spriteController, bool enabled, ControllerType type) { // using 'new' to access non-public constructor - std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>( - new PointerController(policy, looper, spriteController, enabled)); + std::shared_ptr<PointerController> controller; + switch (type) { + case ControllerType::MOUSE: + controller = std::shared_ptr<PointerController>( + new MousePointerController(policy, looper, spriteController, enabled)); + break; + case ControllerType::LEGACY: + default: + controller = std::shared_ptr<PointerController>( + new PointerController(policy, looper, spriteController, enabled)); + break; + } /* * Now we need to hook up the constructed PointerController object to its callbacks. @@ -375,4 +388,13 @@ std::string PointerController::dump() { return dump; } +// --- MousePointerController --- + +MousePointerController::MousePointerController(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, + SpriteController& spriteController, bool enabled) + : PointerController(policy, looper, spriteController, enabled) { + PointerController::setPresentation(Presentation::POINTER); +} + } // namespace android diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index aa7ca3c52ecf..08e19a096d87 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -47,7 +47,8 @@ class PointerController : public PointerControllerInterface { public: static std::shared_ptr<PointerController> create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, - SpriteController& spriteController, bool enabled); + SpriteController& spriteController, bool enabled, + ControllerType type = ControllerType::LEGACY); ~PointerController() override; @@ -75,7 +76,7 @@ public: void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos) REQUIRES(getLock()); - std::string dump(); + std::string dump() override; protected: using WindowListenerConsumer = @@ -87,10 +88,10 @@ protected: WindowListenerConsumer registerListener, WindowListenerConsumer unregisterListener); -private: PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, SpriteController& spriteController, bool enabled); +private: friend PointerControllerContext::LooperCallback; friend PointerControllerContext::MessageHandler; @@ -135,6 +136,24 @@ private: void clearSpotsLocked() REQUIRES(getLock()); }; +class MousePointerController : public PointerController { +public: + /** A version of PointerController that controls one mouse pointer. */ + MousePointerController(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, SpriteController& spriteController, + bool enabled); + + void setPresentation(Presentation) override { + LOG_ALWAYS_FATAL("Should not be called"); + } + void setSpots(const PointerCoords*, const uint32_t*, BitSet32, int32_t) override { + LOG_ALWAYS_FATAL("Should not be called"); + } + void clearSpots() override { + LOG_ALWAYS_FATAL("Should not be called"); + } +}; + } // namespace android #endif // _UI_POINTER_CONTROLLER_H diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 4c9436b4e151..89a8dd95d3c3 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -327,4 +327,12 @@ <!-- Whether wifi is always requested by default. --> <bool name="def_enable_wifi_always_requested">false</bool> + + <!-- Default for Settings.BATTERY_CHARGING_STATE_UPDATE_DELAY in millisecond. + -1 means system internal default value is used. --> + <integer name="def_battery_charging_state_update_delay_ms">-1</integer> + + <!-- Default for Settings.BATTERY_CHARGING_STATE_ENFORCE_LEVEL. + -1 means system internal default value is used. --> + <integer name="def_battery_charging_state_enforce_level">-1</integer> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 785003a355fc..b0abf92ffe08 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3792,7 +3792,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 223; + private static final int SETTINGS_VERSION = 224; private final int mUserId; @@ -5965,8 +5965,23 @@ public class SettingsProvider extends ContentProvider { // Do nothing. Leave the value as is. } } + } + + currentVersion = 223; - currentVersion = 223; + // Version 223: make charging constraint update criteria customizable. + if (currentVersion == 223) { + initGlobalSettingsDefaultValLocked( + Global.BATTERY_CHARGING_STATE_UPDATE_DELAY, + getContext().getResources().getInteger( + R.integer.def_battery_charging_state_update_delay_ms)); + + initGlobalSettingsDefaultValLocked( + Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL, + getContext().getResources().getInteger( + R.integer.def_battery_charging_state_enforce_level) + ); + currentVersion = 224; } // vXXX: Add new settings above this point. diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 1f171ba48343..2e174e267bde 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -145,6 +145,7 @@ public class SettingsBackupTest { Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, Settings.Global.AUTO_TIME_ZONE_EXPLICIT, Settings.Global.AVERAGE_TIME_TO_DISCHARGE, + Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL, Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY, Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME, Settings.Global.BROADCAST_BG_CONSTANTS, diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a9027deccfda..069ba6cd02aa 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -53,6 +53,14 @@ flag { } flag { + name: "keyguard_bottom_area_refactor" + namespace: "systemui" + description: "Bottom area of keyguard refactor move into KeyguardRootView. Includes " + "lock icon and others." + bug: "290652751" +} + +flag { name: "visual_interruptions_refactor" namespace: "systemui" description: "Enables the refactored version of the code to decide when notifications " diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index f2b7b3290f96..56970d7e3005 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -19,6 +19,7 @@ package com.android.systemui.bouncer.ui.composable import android.app.AlertDialog import android.app.Dialog import android.content.DialogInterface +import android.content.res.Configuration import androidx.compose.animation.Crossfade import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.snap @@ -50,6 +51,7 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -63,6 +65,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource @@ -136,7 +139,7 @@ private fun SceneScope.BouncerScene( modifier: Modifier = Modifier, ) { val backgroundColor = MaterialTheme.colorScheme.surface - val windowSizeClass = LocalWindowSizeClass.current + val layout = calculateLayout() Box(modifier) { Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) { @@ -146,22 +149,30 @@ private fun SceneScope.BouncerScene( val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize() val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible - when { - windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded -> + when (layout) { + Layout.STANDARD -> + Bouncer( + viewModel = viewModel, + dialogFactory = dialogFactory, + isUserInputAreaVisible = true, + modifier = childModifier, + ) + Layout.SIDE_BY_SIDE -> SideBySide( viewModel = viewModel, dialogFactory = dialogFactory, + isUserSwitcherVisible = isFullScreenUserSwitcherEnabled, modifier = childModifier, ) - isFullScreenUserSwitcherEnabled && - windowSizeClass.widthSizeClass == WindowWidthSizeClass.Medium -> + Layout.STACKED -> Stacked( viewModel = viewModel, dialogFactory = dialogFactory, + isUserSwitcherVisible = isFullScreenUserSwitcherEnabled, modifier = childModifier, ) - else -> - Bouncer( + Layout.SPLIT -> + Split( viewModel = viewModel, dialogFactory = dialogFactory, modifier = childModifier, @@ -178,11 +189,10 @@ private fun SceneScope.BouncerScene( private fun Bouncer( viewModel: BouncerViewModel, dialogFactory: BouncerSceneDialogFactory, + isUserInputAreaVisible: Boolean, modifier: Modifier = Modifier, ) { val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState() - val authMethodViewModel: AuthMethodBouncerViewModel? by - viewModel.authMethodViewModel.collectAsState() val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState() var dialog: Dialog? by remember { mutableStateOf(null) } @@ -204,25 +214,11 @@ private fun Bouncer( } Box(Modifier.weight(1f)) { - when (val nonNullViewModel = authMethodViewModel) { - is PinBouncerViewModel -> - PinBouncer( - viewModel = nonNullViewModel, - modifier = Modifier.align(Alignment.Center), - ) - is PasswordBouncerViewModel -> - PasswordBouncer( - viewModel = nonNullViewModel, - modifier = Modifier.align(Alignment.Center), - ) - is PatternBouncerViewModel -> - PatternBouncer( - viewModel = nonNullViewModel, - modifier = - Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false) - .align(Alignment.BottomCenter), - ) - else -> Unit + if (isUserInputAreaVisible) { + UserInputArea( + viewModel = viewModel, + modifier = Modifier.align(Alignment.Center), + ) } } @@ -265,6 +261,40 @@ private fun Bouncer( } } +/** + * Renders the user input area, where the user interacts with the UI to enter their credentials. + * + * For example, this can be the pattern input area, the password text box, or pin pad. + */ +@Composable +private fun UserInputArea( + viewModel: BouncerViewModel, + modifier: Modifier = Modifier, +) { + val authMethodViewModel: AuthMethodBouncerViewModel? by + viewModel.authMethodViewModel.collectAsState() + + when (val nonNullViewModel = authMethodViewModel) { + is PinBouncerViewModel -> + PinBouncer( + viewModel = nonNullViewModel, + modifier = modifier, + ) + is PasswordBouncerViewModel -> + PasswordBouncer( + viewModel = nonNullViewModel, + modifier = modifier, + ) + is PatternBouncerViewModel -> + PatternBouncer( + viewModel = nonNullViewModel, + modifier = + Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier) + ) + else -> Unit + } +} + /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */ @Composable private fun UserSwitcher( @@ -287,62 +317,57 @@ private fun UserSwitcher( ) } - UserSwitcherDropdown( - items = dropdownItems, - ) - } -} - -@Composable -private fun UserSwitcherDropdown( - items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>, -) { - val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) } - - items.firstOrNull()?.let { firstDropdownItem -> - Spacer(modifier = Modifier.height(40.dp)) + val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) } + + dropdownItems.firstOrNull()?.let { firstDropdownItem -> + Spacer(modifier = Modifier.height(40.dp)) + + Box { + PlatformButton( + modifier = + Modifier + // Remove the built-in padding applied inside PlatformButton: + .padding(vertical = 0.dp) + .width(UserSwitcherDropdownWidth) + .height(UserSwitcherDropdownHeight), + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHighest, + contentColor = MaterialTheme.colorScheme.onSurface, + ), + onClick = { setDropdownExpanded(!isDropdownExpanded) }, + ) { + val context = LocalContext.current + Text( + text = checkNotNull(firstDropdownItem.text.loadText(context)), + style = MaterialTheme.typography.headlineSmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) - Box { - PlatformButton( - modifier = - Modifier - // Remove the built-in padding applied inside PlatformButton: - .padding(vertical = 0.dp) - .width(UserSwitcherDropdownWidth) - .height(UserSwitcherDropdownHeight), - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.surfaceContainerHighest, - contentColor = MaterialTheme.colorScheme.onSurface, - ), - onClick = { setDropdownExpanded(!isDropdownExpanded) }, - ) { - val context = LocalContext.current - Text( - text = checkNotNull(firstDropdownItem.text.loadText(context)), - style = MaterialTheme.typography.headlineSmall, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) + Spacer(modifier = Modifier.weight(1f)) - Spacer(modifier = Modifier.weight(1f)) + Icon( + imageVector = Icons.Default.KeyboardArrowDown, + contentDescription = null, + modifier = Modifier.size(32.dp), + ) + } - Icon( - imageVector = Icons.Default.KeyboardArrowDown, - contentDescription = null, - modifier = Modifier.size(32.dp), + UserSwitcherDropdownMenu( + isExpanded = isDropdownExpanded, + items = dropdownItems, + onDismissed = { setDropdownExpanded(false) }, ) } - - UserSwitcherDropdownMenu( - isExpanded = isDropdownExpanded, - items = items, - onDismissed = { setDropdownExpanded(false) }, - ) } } } +/** + * Renders the dropdowm menu that displays the actual users and/or user actions that can be + * selected. + */ @Composable private fun UserSwitcherDropdownMenu( isExpanded: Boolean, @@ -396,19 +421,47 @@ private fun UserSwitcherDropdownMenu( } /** - * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap - * anywhere on the background to flip their positions. + * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable + * by double-tapping on the side. */ @Composable -private fun SideBySide( +private fun Split( viewModel: BouncerViewModel, dialogFactory: BouncerSceneDialogFactory, modifier: Modifier = Modifier, ) { + SwappableLayout( + startContent = { startContentModifier -> + Bouncer( + viewModel = viewModel, + dialogFactory = dialogFactory, + isUserInputAreaVisible = false, + modifier = startContentModifier, + ) + }, + endContent = { endContentModifier -> + UserInputArea( + viewModel = viewModel, + modifier = endContentModifier, + ) + }, + modifier = modifier + ) +} + +/** + * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background + * to flip their positions. + */ +@Composable +private fun SwappableLayout( + startContent: @Composable (Modifier) -> Unit, + endContent: @Composable (Modifier) -> Unit, + modifier: Modifier = Modifier, +) { val layoutDirection = LocalLayoutDirection.current val isLeftToRight = layoutDirection == LayoutDirection.Ltr - val (isUserSwitcherFirst, setUserSwitcherFirst) = - rememberSaveable(isLeftToRight) { mutableStateOf(isLeftToRight) } + val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) } Row( modifier = @@ -416,9 +469,8 @@ private fun SideBySide( detectTapGestures( onDoubleTap = { offset -> // Depending on where the user double tapped, switch the elements such that - // the bouncer contents element is closer to the side that was double - // tapped. - setUserSwitcherFirst(offset.x > size.width / 2) + // the endContent is closer to the side that was double tapped. + setSwapped(offset.x < size.width / 2) } ) }, @@ -426,39 +478,30 @@ private fun SideBySide( val animatedOffset by animateFloatAsState( targetValue = - if (isUserSwitcherFirst) { - // When the user switcher is first, both elements have their natural - // placement so they are not offset in any way. + if (!isSwapped) { + // When startContent is first, both elements have their natural placement so + // they are not offset in any way. 0f } else if (isLeftToRight) { - // Since the user switcher is not first, the elements have to be swapped - // horizontally. In the case of LTR locales, this means pushing the user - // switcher to the right, hence the positive number. + // Since startContent is not first, the elements have to be swapped + // horizontally. In the case of LTR locales, this means pushing startContent + // to the right, hence the positive number. 1f } else { - // Since the user switcher is not first, the elements have to be swapped - // horizontally. In the case of RTL locale, this means pushing the user - // switcher to the left, hence the negative number. + // Since startContent is not first, the elements have to be swapped + // horizontally. In the case of RTL locales, this means pushing startContent + // to the left, hence the negative number. -1f }, label = "offset", ) - val userSwitcherModifier = + startContent( Modifier.fillMaxHeight().weight(1f).graphicsLayer { translationX = size.width * animatedOffset alpha = animatedAlpha(animatedOffset) } - if (viewModel.isUserSwitcherVisible) { - UserSwitcher( - viewModel = viewModel, - modifier = userSwitcherModifier, - ) - } else { - Box( - modifier = userSwitcherModifier, - ) - } + ) Box( modifier = @@ -469,41 +512,124 @@ private fun SideBySide( alpha = animatedAlpha(animatedOffset) } ) { + endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter)) + } + } +} + +/** + * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap + * anywhere on the background to flip their positions. + */ +@Composable +private fun SideBySide( + viewModel: BouncerViewModel, + dialogFactory: BouncerSceneDialogFactory, + isUserSwitcherVisible: Boolean, + modifier: Modifier = Modifier, +) { + SwappableLayout( + startContent = { startContentModifier -> + if (isUserSwitcherVisible) { + UserSwitcher( + viewModel = viewModel, + modifier = startContentModifier, + ) + } else { + Box( + modifier = startContentModifier, + ) + } + }, + endContent = { endContentModifier -> Bouncer( viewModel = viewModel, dialogFactory = dialogFactory, - modifier = Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter), + isUserInputAreaVisible = true, + modifier = endContentModifier, ) - } - } + }, + modifier = modifier, + ) } -/** Arranges the bouncer contents and user switcher contents one on top of the other. */ +/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */ @Composable private fun Stacked( viewModel: BouncerViewModel, dialogFactory: BouncerSceneDialogFactory, + isUserSwitcherVisible: Boolean, modifier: Modifier = Modifier, ) { Column( modifier = modifier, ) { - UserSwitcher( - viewModel = viewModel, - modifier = Modifier.fillMaxWidth().weight(1f), - ) + if (isUserSwitcherVisible) { + UserSwitcher( + viewModel = viewModel, + modifier = Modifier.fillMaxWidth().weight(1f), + ) + } + Bouncer( viewModel = viewModel, dialogFactory = dialogFactory, + isUserInputAreaVisible = true, modifier = Modifier.fillMaxWidth().weight(1f), ) } } +@Composable +private fun calculateLayout(): Layout { + val windowSizeClass = LocalWindowSizeClass.current + val width = windowSizeClass.widthSizeClass + val height = windowSizeClass.heightSizeClass + val isLarge = width > WindowWidthSizeClass.Compact && height > WindowHeightSizeClass.Compact + val isTall = + when (height) { + WindowHeightSizeClass.Expanded -> width < WindowWidthSizeClass.Expanded + WindowHeightSizeClass.Medium -> width < WindowWidthSizeClass.Medium + else -> false + } + val isSquare = + when (width) { + WindowWidthSizeClass.Compact -> height == WindowHeightSizeClass.Compact + WindowWidthSizeClass.Medium -> height == WindowHeightSizeClass.Medium + WindowWidthSizeClass.Expanded -> height == WindowHeightSizeClass.Expanded + else -> false + } + val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE + + return when { + // Small and tall devices (i.e. phone/folded in portrait) or square device not in landscape + // mode (unfolded with hinge along horizontal plane). + (!isLarge && isTall) || (isSquare && !isLandscape) -> Layout.STANDARD + // Small and wide devices (i.e. phone/folded in landscape). + !isLarge -> Layout.SPLIT + // Large and tall devices (i.e. tablet in portrait). + isTall -> Layout.STACKED + // Large and wide/square devices (i.e. tablet in landscape, unfolded). + else -> Layout.SIDE_BY_SIDE + } +} + interface BouncerSceneDialogFactory { operator fun invoke(): AlertDialog } +/** Enumerates all known adaptive layout configurations. */ +private enum class Layout { + /** The default UI with the bouncer laid out normally. */ + STANDARD, + /** The bouncer is displayed vertically stacked with the user switcher. */ + STACKED, + /** The bouncer is displayed side-by-side with the user switcher or an empty space. */ + SIDE_BY_SIDE, + /** The bouncer is split in two with both sides shown side-by-side. */ + SPLIT, +} + /** * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of * the two reaches a stopping point but `0` in the middle of the transition. 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 2e93a09deb30..0da562bcb3bb 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 @@ -161,7 +161,8 @@ private fun SceneTransitionObservableTransitionState.toModel(): ObservableTransi fromScene = fromScene.toModel().key, toScene = toScene.toModel().key, progress = progress, - isUserInputDriven = isUserInputDriven, + isInitiatedByUserInput = isInitiatedByUserInput, + isUserInputOngoing = isUserInputOngoing, ) } } 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 88944f10eab9..199832bc4ab6 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 @@ -108,7 +108,7 @@ private fun CoroutineScope.animate( ) { val fromScene = layoutImpl.state.transitionState.currentScene val isUserInput = - (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven + (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput ?: false val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec @@ -119,9 +119,23 @@ private fun CoroutineScope.animate( val targetProgress = if (reversed) 0f else 1f val transition = if (reversed) { - OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable) + OneOffTransition( + fromScene = target, + toScene = fromScene, + currentScene = target, + isInitiatedByUserInput = isUserInput, + isUserInputOngoing = false, + animatable = animatable, + ) } else { - OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable) + OneOffTransition( + fromScene = fromScene, + toScene = target, + currentScene = target, + isInitiatedByUserInput = isUserInput, + isUserInputOngoing = false, + animatable = animatable, + ) } // Change the current layout state to use this new transition. @@ -142,7 +156,8 @@ private class OneOffTransition( override val fromScene: SceneKey, override val toScene: SceneKey, override val currentScene: SceneKey, - override val isUserInputDriven: Boolean, + override val isInitiatedByUserInput: Boolean, + override val isUserInputOngoing: Boolean, private val animatable: Animatable<Float, AnimationVector1D>, ) : TransitionState.Transition { override val progress: Float diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index ccdec6ea8c5e..1b79dbdee510 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -52,7 +52,14 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isUserInputDriven: Boolean, + val isInitiatedByUserInput: Boolean, + + /** + * Whether user input is currently driving the transition. For example, if a user is + * dragging a pointer, this emits true. Once they lift their finger, this emits false while + * the transition completes/settles. + */ + val isUserInputOngoing: Flow<Boolean>, ) : ObservableTransitionState() } @@ -73,7 +80,8 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans fromScene = state.fromScene, toScene = state.toScene, progress = snapshotFlow { state.progress }, - isUserInputDriven = state.isUserInputDriven, + isInitiatedByUserInput = state.isInitiatedByUserInput, + isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, ) } } 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 7a21211c3dde..b9f83c545122 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 @@ -70,6 +70,9 @@ sealed interface TransitionState { val progress: Float /** Whether the transition was triggered by user input rather than being programmatic. */ - val isUserInputDriven: Boolean + val isInitiatedByUserInput: Boolean + + /** Whether user input is currently driving the transition. */ + val isUserInputOngoing: Boolean } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index 8b79c282af9a..877ac095af4e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -508,7 +508,7 @@ class SceneGestureHandler( return offset / distance } - override val isUserInputDriven = true + override val isInitiatedByUserInput = true /** The current offset caused by the drag gesture. */ var dragOffset by mutableFloatStateOf(0f) @@ -519,6 +519,10 @@ class SceneGestureHandler( */ var isAnimatingOffset by mutableStateOf(false) + // If we are not animating offset, it means the offset is being driven by the user's finger. + override val isUserInputOngoing: Boolean + get() = !isAnimatingOffset + /** The animatable used to animate the offset once the user lifted its finger. */ val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold) 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 4a6066f5c664..58d853ef5a00 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 @@ -136,7 +136,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() // Release the finger. We should now be animating back to A (currentScene = SceneA) given // that 55dp < positional threshold. @@ -148,7 +148,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() // Wait for the animation to finish. We should now be in scene A. rule.waitForIdle() @@ -170,7 +170,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() // Release the finger. We should now be animating to C (currentScene = SceneC) given // that 56dp >= positional threshold. @@ -182,7 +182,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isUserInputDriven).isTrue() + assertThat(transition.isInitiatedByUserInput).isTrue() // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() diff --git a/packages/SystemUI/src/com/android/systemui/aconfig/AConfigModule.kt b/packages/SystemUI/src/com/android/systemui/aconfig/AConfigModule.kt deleted file mode 100644 index fa61bba6922d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/aconfig/AConfigModule.kt +++ /dev/null @@ -1,35 +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.systemui.aconfig - -import com.android.systemui.FeatureFlags -import com.android.systemui.FeatureFlagsImpl -import com.android.systemui.dagger.SysUISingleton -import dagger.Module -import dagger.Provides - -@Module -abstract class AConfigModule { - @Module - companion object { - @Provides - @SysUISingleton - fun providesImpl(): FeatureFlags { - return FeatureFlagsImpl() - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index e69eced0de45..1ac4163649a4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -489,6 +489,10 @@ public class AuthContainerView extends LinearLayout public void onAttachedToWindow() { super.onAttachedToWindow(); + if (mContainerState == STATE_ANIMATING_OUT) { + return; + } + mWakefulnessLifecycle.addObserver(this); mPanelInteractionDetector.enable( () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED)); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt index 2bb19cdadc29..f51379940640 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt @@ -26,8 +26,6 @@ import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.isDefaultOrientation import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags import com.android.systemui.log.SideFpsLogger import com.android.systemui.res.R import java.util.Optional @@ -48,7 +46,6 @@ constructor( fingerprintPropertyRepository: FingerprintPropertyRepository, windowManager: WindowManager, displayStateInteractor: DisplayStateInteractor, - featureFlags: FeatureFlagsClassic, fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>, private val logger: SideFpsLogger, ) { @@ -65,14 +62,11 @@ constructor( val isAvailable: Flow<Boolean> = fingerprintPropertyRepository.sensorType.map { it == FingerprintSensorType.POWER_BUTTON } - val authenticationDuration: Flow<Long> = - flowOf(context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L) + val authenticationDuration: Long = + context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L val isProlongedTouchRequiredForAuthentication: Flow<Boolean> = - if ( - fingerprintInteractiveToAuthProvider.isEmpty || - !featureFlags.isEnabled(Flags.REST_TO_UNLOCK) - ) { + if (fingerprintInteractiveToAuthProvider.isEmpty) { flowOf(false) } else { combine( diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt index 9cab17e9f6c4..5c4ee3524a44 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt @@ -1,6 +1,6 @@ package com.android.systemui.communal.data.repository -import com.android.systemui.FeatureFlags +import com.android.systemui.Flags.communalHub import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlagsClassic @@ -29,13 +29,10 @@ interface CommunalRepository { class CommunalRepositoryImpl @Inject constructor( - private val featureFlags: FeatureFlags, private val featureFlagsClassic: FeatureFlagsClassic, ) : CommunalRepository { override val isCommunalEnabled: Boolean - get() = - featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && - featureFlags.communalHub() + get() = featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub() private val _desiredScene: MutableStateFlow<CommunalSceneKey> = MutableStateFlow(CommunalSceneKey.Blank) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 7915088b4642..f3353c7f26dc 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -29,7 +29,6 @@ import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.accessibility.AccessibilityModule; import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule; -import com.android.systemui.aconfig.AConfigModule; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; import com.android.systemui.authentication.AuthenticationModule; @@ -163,7 +162,6 @@ import javax.inject.Named; @Module(includes = { AccessibilityModule.class, AccessibilityRepositoryModule.class, - AConfigModule.class, AppOpsModule.class, AssistModule.class, AuthenticationModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java index 36d789ec049b..87c12b4a5a59 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java @@ -17,6 +17,7 @@ package com.android.systemui.flags; import static com.android.systemui.Flags.exampleFlag; +import static com.android.systemui.Flags.sysuiTeamfood; import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS; import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG; import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS; @@ -38,13 +39,10 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.systemui.FeatureFlags; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.settings.GlobalSettings; -import org.jetbrains.annotations.NotNull; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.Map; @@ -83,7 +81,6 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { private final Map<String, Boolean> mBooleanFlagCache = new ConcurrentHashMap<>(); private final Map<String, String> mStringFlagCache = new ConcurrentHashMap<>(); private final Map<String, Integer> mIntFlagCache = new ConcurrentHashMap<>(); - private final FeatureFlags mGantryFlags; private final Restarter mRestarter; private final ServerFlagReader.ChangeListener mOnPropertiesChanged = @@ -128,7 +125,6 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { @Main Resources resources, ServerFlagReader serverFlagReader, @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags, - FeatureFlags gantryFlags, Restarter restarter) { mFlagManager = flagManager; mContext = context; @@ -137,7 +133,6 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { mSystemProperties = systemProperties; mServerFlagReader = serverFlagReader; mAllFlags = allFlags; - mGantryFlags = gantryFlags; mRestarter = restarter; } @@ -155,16 +150,16 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { } @Override - public boolean isEnabled(@NotNull UnreleasedFlag flag) { + public boolean isEnabled(@NonNull UnreleasedFlag flag) { return isEnabledInternal(flag); } @Override - public boolean isEnabled(@NotNull ReleasedFlag flag) { + public boolean isEnabled(@NonNull ReleasedFlag flag) { return isEnabledInternal(flag); } - private boolean isEnabledInternal(@NotNull BooleanFlag flag) { + private boolean isEnabledInternal(@NonNull BooleanFlag flag) { String name = flag.getName(); Boolean value = mBooleanFlagCache.get(name); @@ -266,7 +261,7 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { && !defaultValue && result == null && flag.getTeamfood()) { - return mGantryFlags.sysuiTeamfood(); + return sysuiTeamfood(); } return result == null ? mServerFlagReader.readServerOverride( @@ -539,7 +534,7 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println("can override: true"); - pw.println("teamfood: " + mGantryFlags.sysuiTeamfood()); + pw.println("teamfood: " + sysuiTeamfood()); pw.println("booleans: " + mBooleanFlagCache.size()); pw.println("example_flag: " + exampleFlag()); pw.println("example_shared_flag: " + exampleSharedFlag()); diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index c1106b342cac..49b8ee632d7e 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -296,11 +296,6 @@ object Flags { @JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT = unreleasedFlag("migrate_clocks_to_blueprint") - /** Migrate KeyguardRootView to use composables. */ - // TODO(b/301969856): Tracking Bug. - @JvmField val KEYGUARD_ROOT_VIEW_USE_COMPOSE = - unreleasedFlag("keyguard_root_view_use_compose") - /** Enables preview loading animation in the wallpaper picker. */ // TODO(b/274443705): Tracking Bug @JvmField @@ -532,10 +527,9 @@ object Flags { @Keep @JvmField val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES = - unreleasedFlag( - name = "screen_record_enterprise_policies", + releasedFlag( + name = "enable_screen_record_enterprise_policies", namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER, - teamfood = false ) // TODO(b/293252410) : Tracking Bug diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt index ba4876fff4d1..0bee48a63a28 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt @@ -22,6 +22,8 @@ import com.android.systemui.CoreStartable import com.android.systemui.biometrics.SideFpsController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.view.SideFpsProgressBar import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel import com.android.systemui.log.SideFpsLogger @@ -31,6 +33,7 @@ import com.android.systemui.util.kotlin.Quint import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -47,15 +50,23 @@ constructor( private val sfpsController: dagger.Lazy<SideFpsController>, private val logger: SideFpsLogger, private val commandRegistry: CommandRegistry, + private val featureFlagsClassic: FeatureFlagsClassic, ) : CoreStartable { override fun start() { + if (!featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) { + return + } + // When the rest to unlock feature is disabled by the user, stop any coroutines that are + // not required. + var layoutJob: Job? = null + var progressJob: Job? = null commandRegistry.registerCommand(spfsProgressBarCommand) { SfpsProgressBarCommand() } applicationScope.launch { viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled -> logger.isProlongedTouchRequiredForAuthenticationChanged(enabled) if (enabled) { - launch { + layoutJob = launch { combine( viewModel.isVisible, viewModel.progressBarLocation, @@ -76,9 +87,13 @@ constructor( ) } } - launch { viewModel.progress.collectLatest { view.setProgress(it) } } + progressJob = launch { + viewModel.progress.collectLatest { view.setProgress(it) } + } } else { view.hide() + layoutJob?.cancel() + progressJob?.cancel() } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt index f8996b7a8a17..a0f5baff29ba 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt @@ -26,6 +26,8 @@ import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.isDefaultOrientation import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus @@ -34,13 +36,17 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati import com.android.systemui.res.R import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @SysUISingleton @@ -52,10 +58,12 @@ constructor( private val sfpsSensorInteractor: SideFpsSensorInteractor, displayStateInteractor: DisplayStateInteractor, @Application private val applicationScope: CoroutineScope, + private val featureFlagsClassic: FeatureFlagsClassic, ) { private val _progress = MutableStateFlow(0.0f) private val _visible = MutableStateFlow(false) private var _animator: ValueAnimator? = null + private var animatorJob: Job? = null private fun onFingerprintCaptureCompleted() { _visible.value = false @@ -147,26 +155,32 @@ constructor( sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication init { - applicationScope.launch { - combine( - sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication, - sfpsSensorInteractor.authenticationDuration, - ::Pair - ) - .collectLatest { (enabled, authDuration) -> - if (!enabled) return@collectLatest + if (featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) { + launchAnimator() + } + } - launch { - fpAuthRepository.authenticationStatus.collectLatest { authStatus -> + private fun launchAnimator() { + applicationScope.launch { + sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication.collectLatest { enabled + -> + if (!enabled) { + animatorJob?.cancel() + return@collectLatest + } + animatorJob = + fpAuthRepository.authenticationStatus + .onEach { authStatus -> when (authStatus) { is AcquiredFingerprintAuthenticationStatus -> { if (authStatus.fingerprintCaptureStarted) { - _visible.value = true _animator?.cancel() _animator = ValueAnimator.ofFloat(0.0f, 1.0f) - .setDuration(authDuration) + .setDuration( + sfpsSensorInteractor.authenticationDuration + ) .apply { addUpdateListener { _progress.value = it.animatedValue as Float @@ -196,8 +210,9 @@ constructor( else -> Unit } } - } - } + .onCompletion { _animator?.cancel() } + .launchIn(applicationScope) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 7353379b2e79..b1440031a2a0 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -26,9 +26,12 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -105,6 +108,26 @@ constructor( initialValue = null, ) + /** + * Whether user input is ongoing for the current transition. For example, if the user is swiping + * their finger to transition between scenes, this value will be true while their finger is on + * the screen, then false for the rest of the transition. + */ + @OptIn(ExperimentalCoroutinesApi::class) + val isTransitionUserInputOngoing: StateFlow<Boolean> = + transitionState + .flatMapLatest { + when (it) { + is ObservableTransitionState.Transition -> it.isUserInputOngoing + is ObservableTransitionState.Idle -> flowOf(false) + } + } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false + ) + /** Whether the scene container is visible. */ val isVisible: StateFlow<Boolean> = repository.isVisible diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt index 9ba02b1aa9a8..49bceefb24e0 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt @@ -17,8 +17,8 @@ package com.android.systemui.scene.shared.flag import androidx.annotation.VisibleForTesting -import com.android.systemui.FeatureFlags import com.android.systemui.Flags as AConfigFlags +import com.android.systemui.Flags.sceneContainer import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlagsClassic @@ -50,7 +50,6 @@ class SceneContainerFlagsImpl @AssistedInject constructor( private val featureFlagsClassic: FeatureFlagsClassic, - featureFlags: FeatureFlags, @Assisted private val isComposeAvailable: Boolean, ) : SceneContainerFlags { @@ -72,7 +71,7 @@ constructor( listOf( AconfigFlagMustBeEnabled( flagName = AConfigFlags.FLAG_SCENE_CONTAINER, - flagValue = featureFlags.sceneContainer(), + flagValue = sceneContainer(), ), ) + classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } + diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt index 3927873f8ba8..f704894e56e2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt @@ -42,6 +42,13 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isUserInputDriven: Boolean, + val isInitiatedByUserInput: Boolean, + + /** + * Whether user input is currently driving the transition. For example, if a user is + * dragging a pointer, this emits true. Once they lift their finger, this emits false while + * the transition completes/settles. + */ + val isUserInputOngoing: Flow<Boolean>, ) : ObservableTransitionState() } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt index a4c4503a6550..b2ffeb3f2925 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt @@ -275,7 +275,7 @@ constructor( when (state) { is ObservableTransitionState.Idle -> false is ObservableTransitionState.Transition -> - state.isUserInputDriven && + state.isInitiatedByUserInput && (state.toScene == sceneKey || state.fromScene == sceneKey) } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index fda4133240c2..225f12536a33 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -810,6 +810,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -825,7 +826,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -842,7 +844,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -860,7 +863,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false + false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -876,6 +880,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Lockscreen, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") @@ -893,6 +898,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, flowOf(.5f), false, + isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 5e7b85779599..2d95b09cbf0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -54,6 +54,7 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.statusbar.events.ANIMATING_OUT import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -211,6 +212,16 @@ open class AuthContainerViewTest : SysuiTestCase() { } @Test + fun testIgnoresAnimatedInWhenDialogAnimatingOut() { + val container = initializeFingerprintContainer(addToView = false) + container.mContainerState = ANIMATING_OUT + container.addToView() + waitForIdleSync() + + verify(callback, never()).onDialogAnimatedIn(anyLong(), anyBoolean()) + } + + @Test fun testDismissBeforeIntroEnd() { val container = initializeFingerprintContainer() waitForIdleSync() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt index 3fbdeec70f1f..67d3a20bd5c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt @@ -37,8 +37,6 @@ import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.logcatLogBuffer -import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.flags.Flags.REST_TO_UNLOCK import com.android.systemui.log.SideFpsLogger import com.android.systemui.res.R import com.android.systemui.util.mockito.whenever @@ -94,7 +92,6 @@ class SideFpsSensorInteractorTest : SysuiTestCase() { whenever(displayStateInteractor.currentRotation).thenReturn(currentRotation) contextDisplayInfo.uniqueId = "current-display" - val featureFlags = FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) } whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser) .thenReturn(isRestToUnlockEnabled) underTest = @@ -103,7 +100,6 @@ class SideFpsSensorInteractorTest : SysuiTestCase() { fingerprintRepository, windowManager, displayStateInteractor, - featureFlags, Optional.of(fingerprintInteractiveToAuthProvider), SideFpsLogger(logcatLogBuffer("SfpsLogger")) ) @@ -136,7 +132,7 @@ class SideFpsSensorInteractorTest : SysuiTestCase() { @Test fun authenticationDurationIsAvailableWhenSFPSSensorIsAvailable() = testScope.runTest { - assertThat(collectLastValue(underTest.authenticationDuration)()) + assertThat(underTest.authenticationDuration) .isEqualTo(context.resources.getInteger(R.integer.config_restToUnlockDuration)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt index f51745b9f17c..b589a2ac8b13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt @@ -22,7 +22,6 @@ import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Resources import android.content.res.Resources.NotFoundException import android.test.suitebuilder.annotation.SmallTest -import com.android.systemui.FakeFeatureFlagsImpl import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -66,7 +65,6 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { private lateinit var broadcastReceiver: BroadcastReceiver private lateinit var clearCacheAction: Consumer<String> private val serverFlagReader = ServerFlagReaderFake() - private val fakeGantryFlags = FakeFeatureFlagsImpl() private val teamfoodableFlagA = UnreleasedFlag(name = "a", namespace = "test", teamfood = true) private val teamfoodableFlagB = ReleasedFlag(name = "b", namespace = "test", teamfood = true) @@ -74,7 +72,6 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", false) flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA) flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB) mFeatureFlagsClassicDebug = @@ -86,7 +83,6 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { resources, serverFlagReader, flagMap, - fakeGantryFlags, restarter ) mFeatureFlagsClassicDebug.init() @@ -134,7 +130,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { @Test fun teamFoodFlag_True() { - fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", true) + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD) assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue() assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue() @@ -149,7 +145,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { .thenReturn(true) whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any())) .thenReturn(false) - fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", true) + mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD) assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue() assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index 27325d3f4ecf..ad2ec72468ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -203,7 +203,8 @@ class KeyguardInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) runCurrent() assertThat(isAnimate).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 88a5c17f4994..9c0456cfdb4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -54,6 +54,7 @@ import com.google.common.truth.Truth.assertWithMessage import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -466,7 +467,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { fromScene = getCurrentSceneInUi(), toScene = to.key, progress = progressFlow, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index 456f1bcb6f73..d669006c68d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -120,7 +121,8 @@ class SceneContainerRepositoryTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 8b23d183f1a6..3f032a45df41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -83,7 +83,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) @@ -121,7 +122,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = underTest.desiredScene.value.key, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(transitionTo).isEqualTo(SceneKey.Shade) @@ -158,7 +160,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) val transitioning by @@ -177,7 +180,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) underTest.setTransitionState(transitionState) @@ -194,7 +198,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) val transitioning by @@ -222,7 +227,8 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(transitioning).isTrue() @@ -231,6 +237,95 @@ class SceneInteractorTest : SysuiTestCase() { } @Test + fun isTransitionUserInputOngoing_idle_false() = + testScope.runTest { + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(SceneKey.Shade) + ) + val isTransitionUserInputOngoing by + collectLastValue(underTest.isTransitionUserInputOngoing) + underTest.setTransitionState(transitionState) + + assertThat(isTransitionUserInputOngoing).isFalse() + } + + @Test + fun isTransitionUserInputOngoing_transition_true() = + testScope.runTest { + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Transition( + fromScene = SceneKey.Shade, + toScene = SceneKey.Lockscreen, + progress = flowOf(0.5f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(true), + ) + ) + val isTransitionUserInputOngoing by + collectLastValue(underTest.isTransitionUserInputOngoing) + underTest.setTransitionState(transitionState) + + assertThat(isTransitionUserInputOngoing).isTrue() + } + + @Test + fun isTransitionUserInputOngoing_updateMidTransition_false() = + testScope.runTest { + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Transition( + fromScene = SceneKey.Shade, + toScene = SceneKey.Lockscreen, + progress = flowOf(0.5f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(true), + ) + ) + val isTransitionUserInputOngoing by + collectLastValue(underTest.isTransitionUserInputOngoing) + underTest.setTransitionState(transitionState) + + assertThat(isTransitionUserInputOngoing).isTrue() + + transitionState.value = + ObservableTransitionState.Transition( + fromScene = SceneKey.Shade, + toScene = SceneKey.Lockscreen, + progress = flowOf(0.6f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + + assertThat(isTransitionUserInputOngoing).isFalse() + } + + @Test + fun isTransitionUserInputOngoing_updateOnIdle_false() = + testScope.runTest { + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Transition( + fromScene = SceneKey.Shade, + toScene = SceneKey.Lockscreen, + progress = flowOf(0.5f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(true), + ) + ) + val isTransitionUserInputOngoing by + collectLastValue(underTest.isTransitionUserInputOngoing) + underTest.setTransitionState(transitionState) + + assertThat(isTransitionUserInputOngoing).isTrue() + + transitionState.value = ObservableTransitionState.Idle(scene = SceneKey.Lockscreen) + + assertThat(isTransitionUserInputOngoing).isFalse() + } + + @Test fun isVisible() = testScope.runTest { val isVisible by collectLastValue(underTest.isVisible) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index c1f2d0cc518f..c0b586195eca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -115,7 +115,8 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason") @@ -128,7 +129,8 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Gone, progress = flowOf(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason") diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt index 0bed4d0d376a..32a38bd1faa1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt @@ -76,7 +76,6 @@ internal class SceneContainerFlagsTest( underTest = SceneContainerFlagsImpl( featureFlagsClassic = featureFlags, - featureFlags = aconfigFlags, isComposeAvailable = testCase.isComposeAvailable, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt index 3a260ae374c6..8b8a62585898 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt @@ -60,6 +60,7 @@ import dagger.BindsInstance import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -495,7 +496,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -532,7 +534,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -568,7 +571,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -844,7 +848,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -881,7 +886,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isUserInputDriven = true, + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -918,7 +924,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -955,7 +962,8 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isUserInputDriven = true, + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) @@ -990,8 +998,9 @@ class ShadeInteractorTest : SysuiTestCase() { ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = SceneKey.QuickSettings, - progress = progress, - isUserInputDriven = true, + progress = MutableStateFlow(0f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), ) ) sceneInteractor.setTransitionState(transitionState) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index f98267b63986..02d15de4d953 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -20,6 +20,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -89,7 +90,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) @@ -107,7 +109,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.QuickSettings, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) @@ -125,7 +128,8 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isUserInputDriven = false, + isInitiatedByUserInput = false, + isUserInputOngoing = flowOf(false), ) ) ) diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index cbaf05bb4e23..a770b66b2506 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -22,3 +22,10 @@ flag { description: "Detect abusive FGS behavior for certain types (camera, mic, media, location)." bug: "295545575" } + +flag { + name: "fgs_boot_completed" + namespace: "backstage_power" + description: "Disable BOOT_COMPLETED broadcast FGS start for certain types" + bug: "296558535" +} diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index a6558e07b2aa..eea13f179b9e 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -14575,9 +14575,10 @@ public class BatteryStatsImpl extends BatteryStats { mModStepMode = 0; } } else { - if (level >= 90) { - // If the battery level is at least 90%, always consider the device to be - // charging even if it happens to go down a level. + if (level >= mConstants.BATTERY_CHARGING_ENFORCE_LEVEL) { + // If the battery level is at least Constants.BATTERY_CHARGING_ENFORCE_LEVEL, + // always consider the device to be charging even if it happens to go down a + // level. changed |= setChargingLocked(true); } else if (!mCharging) { if (mLastChargeStepLevel < level) { @@ -15313,6 +15314,8 @@ public class BatteryStatsImpl extends BatteryStats { public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb"; public static final String KEY_BATTERY_CHARGED_DELAY_MS = "battery_charged_delay_ms"; + public static final String KEY_BATTERY_CHARGING_ENFORCE_LEVEL = + "battery_charging_enforce_level"; public static final String KEY_PER_UID_MODEM_POWER_MODEL = "per_uid_modem_power_model"; public static final String KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION = @@ -15363,6 +15366,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64; private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/ private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */ + private static final int DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL = 90; @PerUidModemPowerModel private static final int DEFAULT_PER_UID_MODEM_MODEL = PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX; @@ -15384,6 +15388,7 @@ public class BatteryStatsImpl extends BatteryStats { public int MAX_HISTORY_FILES; public int MAX_HISTORY_BUFFER; /*Bytes*/ public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS; + public int BATTERY_CHARGING_ENFORCE_LEVEL = DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL; public int PER_UID_MODEM_MODEL = DEFAULT_PER_UID_MODEM_MODEL; public boolean PHONE_ON_EXTERNAL_STATS_COLLECTION = DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION; @@ -15412,6 +15417,9 @@ public class BatteryStatsImpl extends BatteryStats { mResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY), false /* notifyForDescendants */, this); + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL), + false /* notifyForDescendants */, this); updateConstants(); } @@ -15424,6 +15432,12 @@ public class BatteryStatsImpl extends BatteryStats { updateBatteryChargedDelayMsLocked(); } return; + } else if (uri.equals(Settings.Global.getUriFor( + Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL))) { + synchronized (BatteryStatsImpl.this) { + updateBatteryChargingEnforceLevelLocked(); + } + return; } updateConstants(); } @@ -15477,6 +15491,7 @@ public class BatteryStatsImpl extends BatteryStats { DEFAULT_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS); updateBatteryChargedDelayMsLocked(); + updateBatteryChargingEnforceLevelLocked(); onChange(); } @@ -15507,6 +15522,21 @@ public class BatteryStatsImpl extends BatteryStats { } } + private void updateBatteryChargingEnforceLevelLocked() { + int lastChargingEnforceLevel = BATTERY_CHARGING_ENFORCE_LEVEL; + final int level = Settings.Global.getInt(mResolver, + Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL, + -1); + + BATTERY_CHARGING_ENFORCE_LEVEL = level >= 0 ? level : mParser.getInt( + KEY_BATTERY_CHARGING_ENFORCE_LEVEL, DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL); + + if (BATTERY_CHARGING_ENFORCE_LEVEL <= mLastChargeStepLevel + && mLastChargeStepLevel < lastChargingEnforceLevel) { + setChargingLocked(true); + } + } + private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) { KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs; if (oldTimeMs != newTimeMs) { @@ -15541,6 +15571,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(MAX_HISTORY_BUFFER/1024); pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("="); pw.println(BATTERY_CHARGED_DELAY_MS); + pw.print(KEY_BATTERY_CHARGING_ENFORCE_LEVEL); pw.print("="); + pw.println(BATTERY_CHARGING_ENFORCE_LEVEL); pw.print(KEY_PER_UID_MODEM_POWER_MODEL); pw.print("="); pw.println(getPerUidModemModelName(PER_UID_MODEM_MODEL)); pw.print(KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION); pw.print("="); diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 4ebd402863c5..5fd787a1f88c 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -126,10 +126,10 @@ public final class StorageSessionController { connection = new StorageUserConnection(mContext, userId, this); mConnections.put(userId, connection); } - Slog.i(TAG, "Creating and starting session with id: " + sessionId); - connection.startSession(sessionId, deviceFd, vol.getPath().getPath(), - vol.getInternalPath().getPath()); } + Slog.i(TAG, "Creating and starting session with id: " + sessionId); + connection.startSession(sessionId, deviceFd, vol.getPath().getPath(), + vol.getInternalPath().getPath()); } /** diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 176bc28389e5..a7d77304d046 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -86,6 +86,8 @@ namespace input_flags = com::android::input::flags; namespace android { +static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer(); + // The exponent used to calculate the pointer speed scaling factor. // The scaling factor is calculated as 2 ^ (speed * exponent), // where the speed ranges from -7 to + 7 and is supplied by the user. @@ -327,6 +329,8 @@ public: TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr); void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override; bool isInputMethodConnectionActive() override; + std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) override; /* --- InputDispatcherPolicyInterface implementation --- */ @@ -374,8 +378,10 @@ public: virtual PointerIconStyle getCustomPointerIconId(); virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position); - /* --- PointerControllerPolicyInterface implementation --- */ - std::shared_ptr<PointerControllerInterface> createPointerController() override; + /* --- PointerChoreographerPolicyInterface implementation --- */ + std::shared_ptr<PointerControllerInterface> createPointerController( + PointerControllerInterface::ControllerType type) override; + void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override; private: sp<InputManagerInterface> mInputManager; @@ -492,7 +498,9 @@ void NativeInputManager::dump(std::string& dump) { dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n", mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled", mLocked.pointerCaptureRequest.seq); - forEachPointerControllerLocked([&dump](PointerController& pc) { dump += pc.dump(); }); + if (auto pc = mLocked.legacyPointerController.lock(); pc) { + dump += pc->dump(); + } } // release lock dump += "\n"; @@ -537,6 +545,9 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO [&viewports](PointerController& pc) { pc.onDisplayViewportsUpdated(viewports); }); } // release lock + if (ENABLE_POINTER_CHOREOGRAPHER) { + mInputManager->getChoreographer().setDisplayViewports(viewports); + } mInputManager->getReader().requestRefreshConfiguration( InputReaderConfiguration::Change::DISPLAY_INFO); } @@ -721,6 +732,7 @@ void NativeInputManager::forEachPointerControllerLocked( continue; } apply(*pc); + it++; } } @@ -735,9 +747,6 @@ std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerCon if (controller == nullptr) { ensureSpriteControllerLocked(); - static const bool ENABLE_POINTER_CHOREOGRAPHER = - input_flags::enable_pointer_choreographer(); - // Disable the functionality of the legacy PointerController if PointerChoreographer is // enabled. controller = PointerController::create(this, mLooper, *mLocked.spriteController, @@ -749,17 +758,43 @@ std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerCon return controller; } -std::shared_ptr<PointerControllerInterface> NativeInputManager::createPointerController() { +std::shared_ptr<PointerControllerInterface> NativeInputManager::createPointerController( + PointerControllerInterface::ControllerType type) { std::scoped_lock _l(mLock); ensureSpriteControllerLocked(); std::shared_ptr<PointerController> pc = - PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true); + PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true, + type); mLocked.pointerControllers.emplace_back(pc); return pc; } void NativeInputManager::onPointerDisplayIdChanged(int32_t pointerDisplayId, const FloatPoint& position) { + if (ENABLE_POINTER_CHOREOGRAPHER) { + return; + } + JNIEnv* env = jniEnv(); + env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId, + position.x, position.y); + checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged"); +} + +void NativeInputManager::notifyPointerDisplayIdChanged(int32_t pointerDisplayId, + const FloatPoint& position) { + // Notify the Reader so that devices can be reconfigured. + { // acquire lock + std::scoped_lock _l(mLock); + if (mLocked.pointerDisplayId == pointerDisplayId) { + return; + } + mLocked.pointerDisplayId = pointerDisplayId; + ALOGI("%s: pointer displayId set to: %d", __func__, pointerDisplayId); + } // release lock + mInputManager->getReader().requestRefreshConfiguration( + InputReaderConfiguration::Change::DISPLAY_INFO); + + // Notify the system. JNIEnv* env = jniEnv(); env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId, position.x, position.y); @@ -1118,19 +1153,23 @@ void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) { } void NativeInputManager::setPointerDisplayId(int32_t displayId) { - { // acquire lock - std::scoped_lock _l(mLock); + if (ENABLE_POINTER_CHOREOGRAPHER) { + mInputManager->getChoreographer().setDefaultMouseDisplayId(displayId); + } else { + { // acquire lock + std::scoped_lock _l(mLock); - if (mLocked.pointerDisplayId == displayId) { - return; - } + if (mLocked.pointerDisplayId == displayId) { + return; + } - ALOGI("Setting pointer display id to %d.", displayId); - mLocked.pointerDisplayId = displayId; - } // release lock + ALOGI("Setting pointer display id to %d.", displayId); + mLocked.pointerDisplayId = displayId; + } // release lock - mInputManager->getReader().requestRefreshConfiguration( - InputReaderConfiguration::Change::DISPLAY_INFO); + mInputManager->getReader().requestRefreshConfiguration( + InputReaderConfiguration::Change::DISPLAY_INFO); + } } void NativeInputManager::setPointerSpeed(int32_t speed) { @@ -1356,6 +1395,11 @@ bool NativeInputManager::isInputMethodConnectionActive() { return result; } +std::optional<DisplayViewport> NativeInputManager::getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) { + return mInputManager->getChoreographer().getViewportForPointerDevice(associatedDisplayId); +} + bool NativeInputManager::filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) { ATRACE_CALL(); JNIEnv* env = jniEnv(); @@ -1689,6 +1733,9 @@ void NativeInputManager::setStylusButtonMotionEventsEnabled(bool enabled) { } FloatPoint NativeInputManager::getMouseCursorPosition() { + if (ENABLE_POINTER_CHOREOGRAPHER) { + return mInputManager->getChoreographer().getMouseCursorPosition(ADISPLAY_ID_NONE); + } std::scoped_lock _l(mLock); const auto pc = mLocked.legacyPointerController.lock(); if (!pc) return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION}; diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt index 93530cf5b0a9..acaec211440d 100644 --- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt +++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt @@ -16,7 +16,6 @@ package com.android.server.permission.access -import android.app.admin.DevicePolicyManagerInternal import android.content.Context import android.content.pm.PackageManager import android.content.pm.PackageManagerInternal @@ -75,7 +74,7 @@ class AccessCheckingService(context: Context) : SystemService(context) { val userIds = MutableIntSet(userManagerService.userIdsIncludingPreCreated) val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates - val knownPackages = packageManagerInternal.getKnownPackages(packageStates) + val knownPackages = packageManagerInternal.knownPackages val isLeanback = systemConfig.isLeanback val configPermissions = systemConfig.permissions val privilegedPermissionAllowlistPackages = @@ -152,7 +151,7 @@ class AccessCheckingService(context: Context) : SystemService(context) { isSystemUpdated: Boolean ) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates - val knownPackages = packageManagerInternal.getKnownPackages(packageStates) + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { onStorageVolumeMounted( @@ -169,7 +168,7 @@ class AccessCheckingService(context: Context) : SystemService(context) { internal fun onPackageAdded(packageName: String) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates - val knownPackages = packageManagerInternal.getKnownPackages(packageStates) + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { onPackageAdded( @@ -184,7 +183,7 @@ class AccessCheckingService(context: Context) : SystemService(context) { internal fun onPackageRemoved(packageName: String, appId: Int) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates - val knownPackages = packageManagerInternal.getKnownPackages(packageStates) + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { onPackageRemoved( @@ -200,7 +199,7 @@ class AccessCheckingService(context: Context) : SystemService(context) { internal fun onPackageInstalled(packageName: String, userId: Int) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates - val knownPackages = packageManagerInternal.getKnownPackages(packageStates) + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { onPackageInstalled( @@ -216,7 +215,7 @@ class AccessCheckingService(context: Context) : SystemService(context) { internal fun onPackageUninstalled(packageName: String, appId: Int, userId: Int) { val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates - val knownPackages = packageManagerInternal.getKnownPackages(packageStates) + val knownPackages = packageManagerInternal.knownPackages mutateState { with(policy) { onPackageUninstalled( @@ -232,69 +231,50 @@ class AccessCheckingService(context: Context) : SystemService(context) { } internal fun onSystemReady() { - val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates - val knownPackages = packageManagerInternal.getKnownPackages(packageStates) - mutateState { - with(policy) { - onSystemReady(packageStates, disabledSystemPackageStates, knownPackages) - } - } + mutateState { with(policy) { onSystemReady() } } } private val PackageManagerLocal.allPackageStates: Pair<Map<String, PackageState>, Map<String, PackageState>> get() = withUnfilteredSnapshot().use { it.packageStates to it.disabledSystemPackageStates } - private fun PackageManagerInternal.getKnownPackages( - packageStates: Map<String, PackageState> - ): IntMap<Array<String>> = - MutableIntMap<Array<String>>().apply { - this[KnownPackages.PACKAGE_INSTALLER] = - getKnownPackageNames(KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM) - this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] = - getKnownPackageNames( - KnownPackages.PACKAGE_PERMISSION_CONTROLLER, - UserHandle.USER_SYSTEM + private val PackageManagerInternal.knownPackages: IntMap<Array<String>> + get() = + MutableIntMap<Array<String>>().apply { + this[KnownPackages.PACKAGE_INSTALLER] = getKnownPackageNames( + KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM ) - this[KnownPackages.PACKAGE_VERIFIER] = - getKnownPackageNames(KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM) - this[KnownPackages.PACKAGE_SETUP_WIZARD] = - getKnownPackageNames(KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM) - this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] = - getKnownPackageNames( - KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER, - UserHandle.USER_SYSTEM + this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] = getKnownPackageNames( + KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM ) - this[KnownPackages.PACKAGE_CONFIGURATOR] = - getKnownPackageNames(KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM) - this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] = - getKnownPackageNames( - KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER, - UserHandle.USER_SYSTEM + this[KnownPackages.PACKAGE_VERIFIER] = getKnownPackageNames( + KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM ) - this[KnownPackages.PACKAGE_APP_PREDICTOR] = - getKnownPackageNames(KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM) - this[KnownPackages.PACKAGE_COMPANION] = - getKnownPackageNames(KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM) - this[KnownPackages.PACKAGE_RETAIL_DEMO] = - getKnownPackageNames(KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM) - .filter { isProfileOwner(it, packageStates) } - .toTypedArray() - this[KnownPackages.PACKAGE_RECENTS] = - getKnownPackageNames(KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM) - } - - private fun isProfileOwner( - packageName: String, - packageStates: Map<String, PackageState> - ): Boolean { - val appId = packageStates[packageName]?.appId ?: return false - val devicePolicyManagerInternal = - LocalServices.getService(DevicePolicyManagerInternal::class.java) ?: return false - // TODO(b/169395065): Figure out if this flow makes sense in Device Owner mode. - return devicePolicyManagerInternal.isActiveProfileOwner(appId) || - devicePolicyManagerInternal.isActiveDeviceOwner(appId) - } + this[KnownPackages.PACKAGE_SETUP_WIZARD] = getKnownPackageNames( + KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] = getKnownPackageNames( + KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_CONFIGURATOR] = getKnownPackageNames( + KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] = getKnownPackageNames( + KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_APP_PREDICTOR] = getKnownPackageNames( + KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_COMPANION] = getKnownPackageNames( + KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_RETAIL_DEMO] = getKnownPackageNames( + KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM + ) + this[KnownPackages.PACKAGE_RECENTS] = getKnownPackageNames( + KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM + ) + } @OptIn(ExperimentalContracts::class) internal inline fun <T> getState(action: GetStateScope.() -> T): T { diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt index 754f77ec38f9..29fe95c1e252 100644 --- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt @@ -262,17 +262,8 @@ private constructor( forEachSchemePolicy { with(it) { onPackageUninstalled(packageName, appId, userId) } } } - fun MutateStateScope.onSystemReady( - packageStates: Map<String, PackageState>, - disabledSystemPackageStates: Map<String, PackageState>, - knownPackages: IntMap<Array<String>> - ) { - newState.mutateExternalState().apply { - setPackageStates(packageStates) - setDisabledSystemPackageStates(disabledSystemPackageStates) - setKnownPackages(knownPackages) - setSystemReady(true) - } + fun MutateStateScope.onSystemReady() { + newState.mutateExternalState().setSystemReady(true) forEachSchemePolicy { with(it) { onSystemReady() } } } diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index 08ba75397a09..010604f9aaaa 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -1448,15 +1448,6 @@ class AppIdPermissionPolicy : SchemePolicy() { // Special permissions for the system companion device manager. return true } - if ( - permission.isRetailDemo && - packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!! - ) { - // Special permission granted only to the OEM specified retail demo app. - // Note that the original code was passing app ID as UID, so this behavior is kept - // unchanged. - return true - } if (permission.isRecents && packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]!!) { // Special permission for the recents app. return true @@ -1511,27 +1502,6 @@ class AppIdPermissionPolicy : SchemePolicy() { } override fun MutateStateScope.onSystemReady() { - // HACK: PACKAGE_USAGE_STATS is the only permission with the retailDemo protection flag, - // and we have to wait until DevicePolicyManagerService is started to know whether the - // retail demo package is a profile owner so that it can have the permission. - // Since there's no simple callback for profile owner change, and we are deprecating and - // removing the retailDemo protection flag in favor of a proper role soon, we can just - // re-evaluate the permission here, which is also how the old implementation has been - // working. - // TODO: Partially revert ag/22690114 once we can remove support for the retailDemo - // protection flag. - val externalState = newState.externalState - for (packageName in externalState.knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!) { - val appId = externalState.packageStates[packageName]?.appId ?: continue - newState.userStates.forEachIndexed { _, userId, _ -> - evaluatePermissionState( - appId, - userId, - Manifest.permission.PACKAGE_USAGE_STATS, - null - ) - } - } if (!privilegedPermissionAllowlistViolations.isEmpty()) { throw IllegalStateException( "Signature|privileged permissions not in privileged" + diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index dd23d9f006e3..e5291d31d524 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -577,8 +577,14 @@ public class JobSchedulerServiceTest { JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs", createJobInfo(10) .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs", + createJobInfo(3)); spyOn(jobUIDT); when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true); + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); QuotaController quotaController = mService.getQuotaController(); spyOn(quotaController); @@ -595,6 +601,11 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUIDT)); + + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); } @Test @@ -636,7 +647,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -649,7 +660,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -664,7 +675,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -677,7 +688,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -692,7 +703,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -705,7 +716,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -720,7 +731,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -765,7 +776,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -778,7 +789,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -792,7 +803,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -807,7 +818,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -820,7 +831,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); @@ -834,7 +845,7 @@ public class JobSchedulerServiceTest { grantRunUserInitiatedJobsPermission(false); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobUij)); - assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, mService.getMaxJobExecutionTimeMs(jobEj)); assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mService.getMaxJobExecutionTimeMs(jobReg)); |