diff options
186 files changed, 5126 insertions, 1815 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 820d2b0d607e..77b74e9898b8 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -398,8 +398,8 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height, break; } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); diff --git a/core/api/current.txt b/core/api/current.txt index d1d798346c97..12a6f7459bce 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13657,6 +13657,16 @@ package android.content.res { field public int uiMode; } + @FlaggedApi("android.content.res.font_scale_converter_public") public interface FontScaleConverter { + method public float convertDpToSp(float); + method public float convertSpToDp(float); + } + + @FlaggedApi("android.content.res.font_scale_converter_public") public class FontScaleConverterFactory { + method @FlaggedApi("android.content.res.font_scale_converter_public") @AnyThread @Nullable public static android.content.res.FontScaleConverter forScale(float); + method @FlaggedApi("android.content.res.font_scale_converter_public") @AnyThread public static boolean isNonLinearFontScalingActive(float); + } + public class ObbInfo implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -42791,6 +42801,7 @@ package android.telecom { method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(); + method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles(); method public String getDefaultDialerPackage(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle); @@ -46612,6 +46623,8 @@ package android.telephony.ims { method public int getTransportType(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public static final int ATTR_REGISTRATION_TYPE_EMERGENCY = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public static final int ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL = 4; // 0x4 field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR; } @@ -53897,6 +53910,7 @@ package android.view { field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH"; field public static final String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE"; field public static final String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE"; + field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"; field public static final String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"; field public static final String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS"; field public static final String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS"; diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index d395b8cf936e..de330de19560 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -6,7 +6,6 @@ package android { field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS"; field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT"; field public static final String MAKE_UID_VISIBLE = "android.permission.MAKE_UID_VISIBLE"; - field @FlaggedApi("android.companion.flags.companion_transport_apis") public static final String USE_COMPANION_TRANSPORTS = "android.permission.USE_COMPANION_TRANSPORTS"; } } @@ -82,29 +81,6 @@ package android.app.admin { } -package android.companion { - - public final class CompanionDeviceManager { - method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnMessageReceivedListener(@NonNull java.util.concurrent.Executor, int, @NonNull android.companion.CompanionDeviceManager.OnMessageReceivedListener); - method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnTransportsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnTransportsChangedListener); - method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnMessageReceivedListener(int, @NonNull android.companion.CompanionDeviceManager.OnMessageReceivedListener); - method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnTransportsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnTransportsChangedListener); - method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void sendMessage(int, @NonNull byte[], @NonNull int[]); - field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_CONTEXT_SYNC = 1667729539; // 0x63678883 - field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 1669491075; // 0x63826983 - field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_REMOTE_AUTHENTICATION = 1669494629; // 0x63827765 - } - - @FlaggedApi("android.companion.companion_transport_apis") public static interface CompanionDeviceManager.OnMessageReceivedListener { - method public void onMessageReceived(int, @NonNull byte[]); - } - - @FlaggedApi("android.companion.companion_transport_apis") public static interface CompanionDeviceManager.OnTransportsChangedListener { - method public void onTransportsChanged(@NonNull java.util.List<android.companion.AssociationInfo>); - } - -} - package android.content { public abstract class ContentProvider implements android.content.ComponentCallbacks2 { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index fc0fb35b233d..8ce3a8dfb5a2 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -158,6 +158,7 @@ package android { field public static final String INTERACT_ACROSS_USERS_FULL = "android.permission.INTERACT_ACROSS_USERS_FULL"; field public static final String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW"; field public static final String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP"; + field public static final String KEYPHRASE_ENROLLMENT_APPLICATION = "android.permission.KEYPHRASE_ENROLLMENT_APPLICATION"; field public static final String KILL_ALL_BACKGROUND_PROCESSES = "android.permission.KILL_ALL_BACKGROUND_PROCESSES"; field public static final String KILL_UID = "android.permission.KILL_UID"; field public static final String LAUNCH_DEVICE_MANAGER_SETUP = "android.permission.LAUNCH_DEVICE_MANAGER_SETUP"; @@ -214,6 +215,7 @@ package android { field public static final String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; + field public static final String MANAGE_VOICE_KEYPHRASES = "android.permission.MANAGE_VOICE_KEYPHRASES"; field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"; field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN"; field public static final String MANAGE_WEARABLE_SENSING_SERVICE = "android.permission.MANAGE_WEARABLE_SENSING_SERVICE"; @@ -8987,9 +8989,9 @@ package android.media.tv.tuner.frontend { package android.media.voice { public final class KeyphraseModelManager { - method @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public void deleteKeyphraseSoundModel(int, @NonNull java.util.Locale); - method @Nullable @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int, @NonNull java.util.Locale); - method @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public void updateKeyphraseSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel); + method @RequiresPermission(android.Manifest.permission.MANAGE_VOICE_KEYPHRASES) public void deleteKeyphraseSoundModel(int, @NonNull java.util.Locale); + method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_VOICE_KEYPHRASES) public android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int, @NonNull java.util.Locale); + method @RequiresPermission(android.Manifest.permission.MANAGE_VOICE_KEYPHRASES) public void updateKeyphraseSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel); } } @@ -13051,7 +13053,7 @@ package android.service.voice { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(@NonNull String, @NonNull java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback); method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.service.voice.HotwordDetector.Callback); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.HotwordDetector.Callback); - method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager(); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_VOICE_KEYPHRASES) public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager(); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.VisualQueryDetector createVisualQueryDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VisualQueryDetector.Callback); method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setShouldReceiveSandboxedTrainingData(boolean); } @@ -13514,6 +13516,7 @@ package android.telecom { method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts(); method public int getAllPhoneAccountsCount(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean); + method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles(boolean); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}, conditional=true) public int getCallState(); method public android.telecom.PhoneAccountHandle getConnectionManager(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode(); @@ -15723,6 +15726,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException; + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public void registerImsEmergencyRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCrossSimCallingEnabled(boolean) throws android.telephony.ims.ImsException; @@ -15733,6 +15737,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public void unregisterImsEmergencyRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback); } @@ -15765,6 +15770,8 @@ package android.telephony.ims { ctor public ImsRegistrationAttributes.Builder(int); method @NonNull public android.telephony.ims.ImsRegistrationAttributes build(); method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFeatureTags(@NonNull java.util.Set<java.lang.String>); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFlagRegistrationTypeEmergency(); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFlagVirtualRegistrationForEmergencyCall(); method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setSipDetails(@NonNull android.telephony.ims.SipDetails); } @@ -16691,6 +16698,7 @@ package android.telephony.ims.stub { ctor public ImsRegistrationImplBase(@NonNull java.util.concurrent.Executor); method public final void onDeregistered(android.telephony.ims.ImsReasonInfo); method public final void onDeregistered(@Nullable android.telephony.ims.ImsReasonInfo, int, int); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public final void onDeregistered(@Nullable android.telephony.ims.ImsReasonInfo, int, @NonNull android.telephony.ims.ImsRegistrationAttributes); method public final void onDeregistered(@Nullable android.telephony.ims.ImsReasonInfo, @NonNull android.telephony.ims.SipDetails); method public final void onDeregistered(@Nullable android.telephony.ims.ImsReasonInfo, int, int, @NonNull android.telephony.ims.SipDetails); method public final void onRegistered(int); @@ -16699,6 +16707,7 @@ package android.telephony.ims.stub { method public final void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes); method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]); method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public final void onTechnologyChangeFailed(@Nullable android.telephony.ims.ImsReasonInfo, @NonNull android.telephony.ims.ImsRegistrationAttributes); method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String); method public void triggerSipDelegateDeregistration(); method public void updateSipDelegateRegistration(); diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index cef11bb42c3f..b2a28b2127bc 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -1995,8 +1995,12 @@ UnflaggedApi: android.Manifest.permission#ALWAYS_UPDATE_WALLPAPER: New API must be flagged with @FlaggedApi: field android.Manifest.permission.ALWAYS_UPDATE_WALLPAPER UnflaggedApi: android.Manifest.permission#CAMERA_HEADLESS_SYSTEM_USER: New API must be flagged with @FlaggedApi: field android.Manifest.permission.CAMERA_HEADLESS_SYSTEM_USER +UnflaggedApi: android.Manifest.permission#KEYPHRASE_ENROLLMENT_APPLICATION: + New API must be flagged with @FlaggedApi: field android.Manifest.permission.KEYPHRASE_ENROLLMENT_APPLICATION UnflaggedApi: android.Manifest.permission#LAUNCH_PERMISSION_SETTINGS: New API must be flagged with @FlaggedApi: field android.Manifest.permission.LAUNCH_PERMISSION_SETTINGS +UnflaggedApi: android.Manifest.permission#MANAGE_VOICE_KEYPHRASES: + New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_VOICE_KEYPHRASES UnflaggedApi: android.Manifest.permission#READ_INSTALLED_SESSION_PATHS: New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_INSTALLED_SESSION_PATHS UnflaggedApi: android.Manifest.permission#REGISTER_NSD_OFFLOAD_ENGINE: diff --git a/core/api/test-current.txt b/core/api/test-current.txt index a3cd3dc87db3..42daea24593e 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -882,7 +882,6 @@ package android.companion { public final class CompanionDeviceManager { method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void enableSecureTransport(boolean); - field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_PING = 1669362552; // 0x63807378 } public abstract class CompanionDeviceService extends android.app.Service { @@ -2133,7 +2132,7 @@ package android.media.tv.tuner { package android.media.voice { public final class KeyphraseModelManager { - method @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public void setModelDatabaseForTestEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_VOICE_KEYPHRASES) public void setModelDatabaseForTestEnabled(boolean); } } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 287d2bd9e6a7..87c86df6140d 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2909,7 +2909,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras, launcherExtras, dialogInfo, flags, mContext.getOpPackageName(), - UserHandle.myUserId() /* suspendingUserId */, getUserId() /* targetUserId */); + getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 013bcddbb7f3..a510c7704751 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2205,6 +2205,9 @@ public class Notification implements Parcelable private void visitUris(@NonNull Consumer<Uri> visitor) { visitIconUri(visitor, getIcon()); + if (actionIntent != null) { + actionIntent.visitUris(visitor); + } } @Override @@ -2898,6 +2901,21 @@ public class Notification implements Parcelable } } + // allPendingIntents should contain all associated intents after parcelling, but it may also + // contain intents added by the app to extras for their own purposes. We only care about + // checking the intents known and used by system_server, to avoid the confused deputy issue. + List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent, + fullScreenIntent); + for (PendingIntent intent : pendingIntents) { + if (intent != null) { + intent.visitUris(visitor); + } + } + + if (mBubbleMetadata != null) { + mBubbleMetadata.visitUris(visitor); + } + if (extras != null) { visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class)); visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class)); @@ -2969,15 +2987,28 @@ public class Notification implements Parcelable callPerson.visitUris(visitor); } visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class)); - } - if (mBubbleMetadata != null) { - visitIconUri(visitor, mBubbleMetadata.getIcon()); - } + // Extras for MediaStyle. + PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT, + PendingIntent.class); + if (deviceIntent != null) { + deviceIntent.visitUris(visitor); + } - if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { - WearableExtender extender = new WearableExtender(this); - extender.visitUris(visitor); + if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { + WearableExtender extender = new WearableExtender(this); + extender.visitUris(visitor); + } + + if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) { + TvExtender extender = new TvExtender(this); + extender.visitUris(visitor); + } + + if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) { + CarExtender extender = new CarExtender(this); + extender.visitUris(visitor); + } } } @@ -10558,6 +10589,16 @@ public class Notification implements Parcelable } } + private void visitUris(@NonNull Consumer<Uri> visitor) { + visitIconUri(visitor, getIcon()); + if (mPendingIntent != null) { + mPendingIntent.visitUris(visitor); + } + if (mDeleteIntent != null) { + mDeleteIntent.visitUris(visitor); + } + } + /** * Builder to construct a {@link BubbleMetadata} object. */ @@ -11756,6 +11797,9 @@ public class Notification implements Parcelable } private void visitUris(@NonNull Consumer<Uri> visitor) { + if (mDisplayIntent != null) { + mDisplayIntent.visitUris(visitor); + } for (Action action : mActions) { action.visitUris(visitor); } @@ -11908,12 +11952,19 @@ public class Notification implements Parcelable /** * Returns the unread conversation conveyed by this notification. + * * @see #setUnreadConversation(UnreadConversation) */ public UnreadConversation getUnreadConversation() { return mUnreadConversation; } + private void visitUris(@NonNull Consumer<Uri> visitor) { + if (mUnreadConversation != null) { + mUnreadConversation.visitUris(visitor); + } + } + /** * A class which holds the unread messages from a conversation. */ @@ -12065,7 +12116,16 @@ public class Notification implements Parcelable onRead, participants, b.getLong(KEY_TIMESTAMP)); } - }; + + private void visitUris(@NonNull Consumer<Uri> visitor) { + if (mReadPendingIntent != null) { + mReadPendingIntent.visitUris(visitor); + } + if (mReplyPendingIntent != null) { + mReplyPendingIntent.visitUris(visitor); + } + } + } /** * Builder class for {@link CarExtender.UnreadConversation} objects. @@ -12388,6 +12448,15 @@ public class Notification implements Parcelable public boolean isSuppressShowOverApps() { return mSuppressShowOverApps; } + + private void visitUris(@NonNull Consumer<Uri> visitor) { + if (mContentIntent != null) { + mContentIntent.visitUris(visitor); + } + if (mDeleteIntent != null) { + mDeleteIntent.visitUris(visitor); + } + } } /** diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 62209b0fd27d..0261f0a02174 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -44,6 +44,8 @@ import android.content.IntentSender; import android.content.pm.PackageManager.ResolveInfoFlagsBits; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -69,6 +71,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * A description of an Intent and target action to perform with it. Instances @@ -1460,6 +1463,21 @@ public final class PendingIntent implements Parcelable { return sb.toString(); } + /** + * See {@link Intent#visitUris(Consumer)}. + * + * @hide + */ + public void visitUris(@NonNull Consumer<Uri> visitor) { + if (android.app.Flags.visitRiskyUris()) { + Intent intent = Binder.withCleanCallingIdentity(this::getIntent); + + if (intent != null) { + intent.visitUris(visitor); + } + } + } + /** @hide */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 820ff3e308e4..be420debc88f 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -77,6 +77,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemProperties; +import android.os.Trace; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -91,7 +92,6 @@ import com.android.internal.R; import libcore.io.IoUtils; import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -614,11 +614,14 @@ public class WallpaperManager { ColorManagementProxy cmProxy) { if (mService != null) { try { + Trace.beginSection("WPMS.isWallpaperSupported"); if (!mService.isWallpaperSupported(context.getOpPackageName())) { return null; } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); + } finally { + Trace.endSection(); } } synchronized (this) { @@ -629,6 +632,7 @@ public class WallpaperManager { mCachedWallpaper = null; Bitmap currentWallpaper = null; try { + Trace.beginSection("WPMS.getCurrentWallpaperLocked"); currentWallpaper = getCurrentWallpaperLocked( context, which, userId, hardware, cmProxy); } catch (OutOfMemoryError e) { @@ -654,6 +658,8 @@ public class WallpaperManager { // Post-O apps really most sincerely need the permission. throw e; } + } finally { + Trace.endSection(); } if (currentWallpaper != null) { mCachedWallpaper = new CachedWallpaper(currentWallpaper, userId, which); @@ -732,19 +738,15 @@ public class WallpaperManager { try { Bundle params = new Bundle(); + Trace.beginSection("WPMS.getWallpaperWithFeature_" + which); ParcelFileDescriptor pfd = mService.getWallpaperWithFeature( context.getOpPackageName(), context.getAttributionTag(), this, which, params, userId, /* getCropped = */ true); + Trace.endSection(); if (pfd != null) { - try (BufferedInputStream bis = new BufferedInputStream( - new ParcelFileDescriptor.AutoCloseInputStream(pfd))) { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int data; - while ((data = bis.read()) != -1) { - baos.write(data); - } - ImageDecoder.Source src = ImageDecoder.createSource(baos.toByteArray()); + try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + ImageDecoder.Source src = ImageDecoder.createSource(is.readAllBytes()); return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> { // Mutable and hardware config can't be set at the same time. decoder.setMutableRequired(!hardware); @@ -764,13 +766,18 @@ public class WallpaperManager { } private Bitmap getDefaultWallpaper(Context context, @SetWallpaperFlags int which) { + Trace.beginSection("WPMS.getDefaultWallpaper_" + which); Bitmap defaultWallpaper = mDefaultWallpaper; if (defaultWallpaper == null || defaultWallpaper.isRecycled()) { defaultWallpaper = null; + Trace.beginSection("WPMS.openDefaultWallpaper"); try (InputStream is = openDefaultWallpaper(context, which)) { + Trace.endSection(); if (is != null) { BitmapFactory.Options options = new BitmapFactory.Options(); + Trace.beginSection("WPMS.decodeStream"); defaultWallpaper = BitmapFactory.decodeStream(is, null, options); + Trace.endSection(); } } catch (OutOfMemoryError | IOException e) { Log.w(TAG, "Can't decode stream", e); @@ -779,6 +786,7 @@ public class WallpaperManager { synchronized (this) { mDefaultWallpaper = defaultWallpaper; } + Trace.endSection(); return defaultWallpaper; } diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index d5b5f40a6980..b11840e8a931 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMIN import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH; -import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; @@ -219,31 +218,24 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @TestApi public static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN + public static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN /** * Message header assigned to the remote authentication handshakes. * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) public static final int MESSAGE_REQUEST_REMOTE_AUTHENTICATION = 0x63827765; // ?RMA /** * Message header assigned to the telecom context sync metadata. * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) public static final int MESSAGE_REQUEST_CONTEXT_SYNC = 0x63678883; // ?CXS /** * Message header assigned to the permission restore request. * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES /** @@ -905,8 +897,6 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) public interface OnTransportsChangedListener { /** * Invoked when a transport is attached or detached. @@ -925,8 +915,6 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnTransportsChangedListener( @NonNull @CallbackExecutor Executor executor, @@ -947,8 +935,6 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnTransportsChangedListener( @NonNull OnTransportsChangedListener listener) { @@ -969,8 +955,6 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void sendMessage(int messageType, @NonNull byte[] data, @NonNull int[] associationIds) { try { @@ -987,8 +971,6 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) public interface OnMessageReceivedListener { /** * Called when a message is received. @@ -1001,8 +983,6 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnMessageReceivedListener( @NonNull @CallbackExecutor Executor executor, int messageType, @@ -1021,8 +1001,6 @@ public final class CompanionDeviceManager { * * @hide */ - @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS) - @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnMessageReceivedListener(int messageType, @NonNull OnMessageReceivedListener listener) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7af0be3b3e75..183b9b0000d2 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -101,6 +101,7 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.TimeZone; +import java.util.function.Consumer; /** * An intent is an abstract description of an operation to be performed. It @@ -8147,6 +8148,27 @@ public class Intent implements Parcelable, Cloneable { } } + /** + * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission + * grants will need to be issued to ensure the recipient of this object is able to render its + * contents. + * See b/281044385 for more context and examples about what happens when this isn't done + * correctly. + * + * @hide + */ + public void visitUris(@NonNull Consumer<Uri> visitor) { + if (android.app.Flags.visitRiskyUris()) { + visitor.accept(mData); + if (mSelector != null) { + mSelector.visitUris(visitor); + } + if (mOriginalIntent != null) { + mOriginalIntent.visitUris(visitor); + } + } + } + public static Intent getIntentOld(String uri) throws URISyntaxException { Intent intent = getIntentOld(uri, 0); intent.mLocalFlags |= LOCAL_FLAG_FROM_URI; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 6dc8d4738c87..98623de810c4 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -300,8 +300,7 @@ interface IPackageManager { String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, in PersistableBundle appExtras, in PersistableBundle launcherExtras, - in SuspendDialogInfo dialogInfo, int flags, String suspendingPackage, - int suspendingUserId, int targetUserId); + in SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId); String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a8638708824b..82a8c11f024f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -10000,9 +10000,6 @@ public abstract class PackageManager { * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or * {@link android.Manifest.permission#SUSPEND_APPS}. * - * <p> - * <strong>Note:</strong>This API doesn't support cross user suspension and should only be used - * for testing. * @param suspendedPackage The package that has been suspended. * @return Name of the package that suspended the given package. Returns {@code null} if the * given package is not currently suspended and the platform package name - i.e. diff --git a/core/java/android/content/res/FontScaleConverter.java b/core/java/android/content/res/FontScaleConverter.java index 28525e25bdd7..088949e7eec2 100644 --- a/core/java/android/content/res/FontScaleConverter.java +++ b/core/java/android/content/res/FontScaleConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,11 @@ package android.content.res; -import android.annotation.NonNull; -import android.util.MathUtils; -import com.android.internal.annotations.VisibleForTesting; - -import java.util.Arrays; +import android.annotation.FlaggedApi; /** - * A lookup table for non-linear font scaling. Converts font sizes given in "sp" dimensions to a + * A converter for non-linear font scaling. Converts font sizes given in "sp" dimensions to a * "dp" dimension according to a non-linear curve. * * <p>This is meant to improve readability at larger font scales: larger fonts will scale up more @@ -32,131 +28,16 @@ import java.util.Arrays; * * <p>The thinking here is that large fonts are already big enough to read, but we still want to * scale them slightly to preserve the visual hierarchy when compared to smaller fonts. - * - * @hide */ -public class FontScaleConverter { - - @VisibleForTesting - final float[] mFromSpValues; - @VisibleForTesting - final float[] mToDpValues; - +@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) +public interface FontScaleConverter { /** - * Creates a lookup table for the given conversions. - * - * <p>Any "sp" value not in the lookup table will be derived via linear interpolation. - * - * <p>The arrays must be sorted ascending and monotonically increasing. - * - * @param fromSp array of dimensions in SP - * @param toDp array of dimensions in DP that correspond to an SP value in fromSp - * - * @throws IllegalArgumentException if the array lengths don't match or are empty - * @hide + * Converts a dimension in "sp" to "dp". */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public FontScaleConverter(@NonNull float[] fromSp, @NonNull float[] toDp) { - if (fromSp.length != toDp.length || fromSp.length == 0) { - throw new IllegalArgumentException("Array lengths must match and be nonzero"); - } - - mFromSpValues = fromSp; - mToDpValues = toDp; - } + float convertSpToDp(float sp); /** - * Convert a dimension in "dp" back to "sp" using the lookup table. - * - * @hide + * Converts a dimension in "dp" back to "sp". */ - public float convertDpToSp(float dp) { - return lookupAndInterpolate(dp, mToDpValues, mFromSpValues); - } - - /** - * Convert a dimension in "sp" to "dp" using the lookup table. - * - * @hide - */ - public float convertSpToDp(float sp) { - return lookupAndInterpolate(sp, mFromSpValues, mToDpValues); - } - - private static float lookupAndInterpolate( - float sourceValue, - float[] sourceValues, - float[] targetValues - ) { - final float sourceValuePositive = Math.abs(sourceValue); - // TODO(b/247861374): find a match at a higher index? - final float sign = Math.signum(sourceValue); - // We search for exact matches only, even if it's just a little off. The interpolation will - // handle any non-exact matches. - final int index = Arrays.binarySearch(sourceValues, sourceValuePositive); - if (index >= 0) { - // exact match, return the matching dp - return sign * targetValues[index]; - } else { - // must be a value in between index and index + 1: interpolate. - final int lowerIndex = -(index + 1) - 1; - - final float startSp; - final float endSp; - final float startDp; - final float endDp; - - if (lowerIndex >= sourceValues.length - 1) { - // It's past our lookup table. Determine the last elements' scaling factor and use. - startSp = sourceValues[sourceValues.length - 1]; - startDp = targetValues[sourceValues.length - 1]; - - if (startSp == 0) return 0; - - final float scalingFactor = startDp / startSp; - return sourceValue * scalingFactor; - } else if (lowerIndex == -1) { - // It's smaller than the smallest value in our table. Interpolate from 0. - startSp = 0; - startDp = 0; - endSp = sourceValues[0]; - endDp = targetValues[0]; - } else { - startSp = sourceValues[lowerIndex]; - endSp = sourceValues[lowerIndex + 1]; - startDp = targetValues[lowerIndex]; - endDp = targetValues[lowerIndex + 1]; - } - - return sign - * MathUtils.constrainedMap(startDp, endDp, startSp, endSp, sourceValuePositive); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null) return false; - if (!(o instanceof FontScaleConverter)) return false; - FontScaleConverter that = (FontScaleConverter) o; - return Arrays.equals(mFromSpValues, that.mFromSpValues) - && Arrays.equals(mToDpValues, that.mToDpValues); - } - - @Override - public int hashCode() { - int result = Arrays.hashCode(mFromSpValues); - result = 31 * result + Arrays.hashCode(mToDpValues); - return result; - } - - @Override - public String toString() { - return "FontScaleConverter{" - + "fromSpValues=" - + Arrays.toString(mFromSpValues) - + ", toDpValues=" - + Arrays.toString(mToDpValues) - + '}'; - } + float convertDpToSp(float dp); } diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java index 5eb65262918d..5d31cc0f0243 100644 --- a/core/java/android/content/res/FontScaleConverterFactory.java +++ b/core/java/android/content/res/FontScaleConverterFactory.java @@ -16,6 +16,8 @@ package android.content.res; +import android.annotation.AnyThread; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.MathUtils; @@ -24,67 +26,88 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; /** - * Stores lookup tables for creating {@link FontScaleConverter}s at various scales. + * Creates {@link FontScaleConverter}s at various scales. * - * @hide + * Generally you shouldn't need this; you can use {@link + * android.util.TypedValue#applyDimension(int, float, DisplayMetrics)} directly and it will do the + * scaling conversion for you. But for UI frameworks or other situations where you need to do the + * conversion without an Android Context, you can use this class. */ +@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) public class FontScaleConverterFactory { private static final float SCALE_KEY_MULTIPLIER = 100f; + /** @hide */ + // GuardedBy("LOOKUP_TABLES_WRITE_LOCK") but only for writes! @VisibleForTesting - static final SparseArray<FontScaleConverter> LOOKUP_TABLES = new SparseArray<>(); + @NonNull + public static volatile SparseArray<FontScaleConverter> sLookupTables = new SparseArray<>(); + + /** + * This is a write lock only! We don't care about synchronization on reads; they can be a bit + * out of date. But all writes have to be atomic, so we use this similar to a + * CopyOnWriteArrayList. + */ + private static final Object LOOKUP_TABLES_WRITE_LOCK = new Object(); private static float sMinScaleBeforeCurvesApplied = 1.05f; static { // These were generated by frameworks/base/tools/fonts/font-scaling-array-generator.js and // manually tweaked for optimum readability. - put( - /* scaleKey= */ 1.15f, - new FontScaleConverter( - /* fromSp= */ - new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, - /* toDp= */ - new float[] { 9.2f, 11.5f, 13.8f, 16.4f, 19.8f, 21.8f, 25.2f, 30f, 100}) - ); - - put( - /* scaleKey= */ 1.3f, - new FontScaleConverter( - /* fromSp= */ - new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, - /* toDp= */ - new float[] {10.4f, 13f, 15.6f, 18.8f, 21.6f, 23.6f, 26.4f, 30f, 100}) - ); - - put( - /* scaleKey= */ 1.5f, - new FontScaleConverter( - /* fromSp= */ - new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, - /* toDp= */ - new float[] { 12f, 15f, 18f, 22f, 24f, 26f, 28f, 30f, 100}) - ); - - put( - /* scaleKey= */ 1.8f, - new FontScaleConverter( - /* fromSp= */ - new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, - /* toDp= */ - new float[] {14.4f, 18f, 21.6f, 24.4f, 27.6f, 30.8f, 32.8f, 34.8f, 100}) - ); - - put( - /* scaleKey= */ 2f, - new FontScaleConverter( - /* fromSp= */ - new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, - /* toDp= */ - new float[] { 16f, 20f, 24f, 26f, 30f, 34f, 36f, 38f, 100}) - ); - - sMinScaleBeforeCurvesApplied = getScaleFromKey(LOOKUP_TABLES.keyAt(0)) - 0.02f; + synchronized (LOOKUP_TABLES_WRITE_LOCK) { + putInto( + sLookupTables, + /* scaleKey= */ 1.15f, + new FontScaleConverterImpl( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] { 9.2f, 11.5f, 13.8f, 16.4f, 19.8f, 21.8f, 25.2f, 30f, 100}) + ); + + putInto( + sLookupTables, + /* scaleKey= */ 1.3f, + new FontScaleConverterImpl( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] {10.4f, 13f, 15.6f, 18.8f, 21.6f, 23.6f, 26.4f, 30f, 100}) + ); + + putInto( + sLookupTables, + /* scaleKey= */ 1.5f, + new FontScaleConverterImpl( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] { 12f, 15f, 18f, 22f, 24f, 26f, 28f, 30f, 100}) + ); + + putInto( + sLookupTables, + /* scaleKey= */ 1.8f, + new FontScaleConverterImpl( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] {14.4f, 18f, 21.6f, 24.4f, 27.6f, 30.8f, 32.8f, 34.8f, 100}) + ); + + putInto( + sLookupTables, + /* scaleKey= */ 2f, + new FontScaleConverterImpl( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] { 16f, 20f, 24f, 26f, 30f, 34f, 36f, 38f, 100}) + ); + } + + sMinScaleBeforeCurvesApplied = getScaleFromKey(sLookupTables.keyAt(0)) - 0.02f; if (sMinScaleBeforeCurvesApplied <= 1.0f) { throw new IllegalStateException( "You should only apply non-linear scaling to font scales > 1" @@ -100,9 +123,9 @@ public class FontScaleConverterFactory { * * <p>Example usage: * <code>isNonLinearFontScalingActive(getResources().getConfiguration().fontScale)</code> - * - * @hide */ + @FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) + @AnyThread public static boolean isNonLinearFontScalingActive(float fontScale) { return fontScale >= sMinScaleBeforeCurvesApplied; } @@ -113,10 +136,10 @@ public class FontScaleConverterFactory { * @param fontScale the scale factor, usually from {@link Configuration#fontScale}. * * @return a converter for the given scale, or null if non-linear scaling should not be used. - * - * @hide */ + @FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) @Nullable + @AnyThread public static FontScaleConverter forScale(float fontScale) { if (!isNonLinearFontScalingActive(fontScale)) { return null; @@ -128,23 +151,33 @@ public class FontScaleConverterFactory { } // Didn't find an exact match: interpolate between two existing tables - final int index = LOOKUP_TABLES.indexOfKey(getKey(fontScale)); + final int index = sLookupTables.indexOfKey(getKey(fontScale)); if (index >= 0) { // This should never happen, should have been covered by get() above. - return LOOKUP_TABLES.valueAt(index); + return sLookupTables.valueAt(index); } // Didn't find an exact match: interpolate between two existing tables final int lowerIndex = -(index + 1) - 1; final int higherIndex = lowerIndex + 1; - if (lowerIndex < 0 || higherIndex >= LOOKUP_TABLES.size()) { + if (lowerIndex < 0 || higherIndex >= sLookupTables.size()) { // We have gone beyond our bounds and have nothing to interpolate between. Just give // them a straight linear table instead. // This works because when FontScaleConverter encounters a size beyond its bounds, it // calculates a linear fontScale factor using the ratio of the last element pair. - return new FontScaleConverter(new float[] {1f}, new float[] {fontScale}); + FontScaleConverterImpl converter = new FontScaleConverterImpl( + new float[]{1f}, + new float[]{fontScale} + ); + + if (Flags.fontScaleConverterPublic()) { + // Cache for next time. + put(fontScale, converter); + } + + return converter; } else { - float startScale = getScaleFromKey(LOOKUP_TABLES.keyAt(lowerIndex)); - float endScale = getScaleFromKey(LOOKUP_TABLES.keyAt(higherIndex)); + float startScale = getScaleFromKey(sLookupTables.keyAt(lowerIndex)); + float endScale = getScaleFromKey(sLookupTables.keyAt(higherIndex)); float interpolationPoint = MathUtils.constrainedMap( /* rangeMin= */ 0f, /* rangeMax= */ 1f, @@ -152,10 +185,18 @@ public class FontScaleConverterFactory { endScale, fontScale ); - return createInterpolatedTableBetween( - LOOKUP_TABLES.valueAt(lowerIndex), - LOOKUP_TABLES.valueAt(higherIndex), - interpolationPoint); + FontScaleConverter converter = createInterpolatedTableBetween( + sLookupTables.valueAt(lowerIndex), + sLookupTables.valueAt(higherIndex), + interpolationPoint + ); + + if (Flags.fontScaleConverterPublic()) { + // Cache for next time. + put(fontScale, converter); + } + + return converter; } } @@ -175,7 +216,7 @@ public class FontScaleConverterFactory { dpInterpolated[i] = MathUtils.lerp(startDp, endDp, interpolationPoint); } - return new FontScaleConverter(commonSpSizes, dpInterpolated); + return new FontScaleConverterImpl(commonSpSizes, dpInterpolated); } private static int getKey(float fontScale) { @@ -187,11 +228,24 @@ public class FontScaleConverterFactory { } private static void put(float scaleKey, @NonNull FontScaleConverter fontScaleConverter) { - LOOKUP_TABLES.put(getKey(scaleKey), fontScaleConverter); + // Dollar-store CopyOnWriteSparseArray, since this is the only write op we need. + synchronized (LOOKUP_TABLES_WRITE_LOCK) { + var newTable = sLookupTables.clone(); + putInto(newTable, scaleKey, fontScaleConverter); + sLookupTables = newTable; + } + } + + private static void putInto( + SparseArray<FontScaleConverter> table, + float scaleKey, + @NonNull FontScaleConverter fontScaleConverter + ) { + table.put(getKey(scaleKey), fontScaleConverter); } @Nullable private static FontScaleConverter get(float scaleKey) { - return LOOKUP_TABLES.get(getKey(scaleKey)); + return sLookupTables.get(getKey(scaleKey)); } } diff --git a/core/java/android/content/res/FontScaleConverterImpl.java b/core/java/android/content/res/FontScaleConverterImpl.java new file mode 100644 index 000000000000..1968c4e53109 --- /dev/null +++ b/core/java/android/content/res/FontScaleConverterImpl.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.res; + +import android.annotation.NonNull; +import android.util.MathUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Arrays; + +/** + * A lookup table for non-linear font scaling. Converts font sizes given in "sp" dimensions to a + * "dp" dimension according to a non-linear curve by interpolating values in a lookup table. + * + * {@see FontScaleConverter} + * + * @hide + */ +// Needs to be public so the Kotlin test can see it +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public class FontScaleConverterImpl implements FontScaleConverter { + + /** @hide */ + @VisibleForTesting + public final float[] mFromSpValues; + /** @hide */ + @VisibleForTesting + public final float[] mToDpValues; + + /** + * Creates a lookup table for the given conversions. + * + * <p>Any "sp" value not in the lookup table will be derived via linear interpolation. + * + * <p>The arrays must be sorted ascending and monotonically increasing. + * + * @param fromSp array of dimensions in SP + * @param toDp array of dimensions in DP that correspond to an SP value in fromSp + * + * @throws IllegalArgumentException if the array lengths don't match or are empty + * @hide + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public FontScaleConverterImpl(@NonNull float[] fromSp, @NonNull float[] toDp) { + if (fromSp.length != toDp.length || fromSp.length == 0) { + throw new IllegalArgumentException("Array lengths must match and be nonzero"); + } + + mFromSpValues = fromSp; + mToDpValues = toDp; + } + + /** + * Convert a dimension in "dp" back to "sp" using the lookup table. + * + * @hide + */ + @Override + public float convertDpToSp(float dp) { + return lookupAndInterpolate(dp, mToDpValues, mFromSpValues); + } + + /** + * Convert a dimension in "sp" to "dp" using the lookup table. + * + * @hide + */ + @Override + public float convertSpToDp(float sp) { + return lookupAndInterpolate(sp, mFromSpValues, mToDpValues); + } + + private static float lookupAndInterpolate( + float sourceValue, + float[] sourceValues, + float[] targetValues + ) { + final float sourceValuePositive = Math.abs(sourceValue); + // TODO(b/247861374): find a match at a higher index? + final float sign = Math.signum(sourceValue); + // We search for exact matches only, even if it's just a little off. The interpolation will + // handle any non-exact matches. + final int index = Arrays.binarySearch(sourceValues, sourceValuePositive); + if (index >= 0) { + // exact match, return the matching dp + return sign * targetValues[index]; + } else { + // must be a value in between index and index + 1: interpolate. + final int lowerIndex = -(index + 1) - 1; + + final float startSp; + final float endSp; + final float startDp; + final float endDp; + + if (lowerIndex >= sourceValues.length - 1) { + // It's past our lookup table. Determine the last elements' scaling factor and use. + startSp = sourceValues[sourceValues.length - 1]; + startDp = targetValues[sourceValues.length - 1]; + + if (startSp == 0) return 0; + + final float scalingFactor = startDp / startSp; + return sourceValue * scalingFactor; + } else if (lowerIndex == -1) { + // It's smaller than the smallest value in our table. Interpolate from 0. + startSp = 0; + startDp = 0; + endSp = sourceValues[0]; + endDp = targetValues[0]; + } else { + startSp = sourceValues[lowerIndex]; + endSp = sourceValues[lowerIndex + 1]; + startDp = targetValues[lowerIndex]; + endDp = targetValues[lowerIndex + 1]; + } + + return sign + * MathUtils.constrainedMap(startDp, endDp, startSp, endSp, sourceValuePositive); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (!(o instanceof FontScaleConverterImpl)) return false; + FontScaleConverterImpl that = (FontScaleConverterImpl) o; + return Arrays.equals(mFromSpValues, that.mFromSpValues) + && Arrays.equals(mToDpValues, that.mToDpValues); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(mFromSpValues); + result = 31 * result + Arrays.hashCode(mToDpValues); + return result; + } + + @Override + public String toString() { + return "FontScaleConverter{" + + "fromSpValues=" + + Arrays.toString(mFromSpValues) + + ", toDpValues=" + + Arrays.toString(mToDpValues) + + '}'; + } +} diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig index 3a00d91bfb9f..db81e847d6bd 100644 --- a/core/java/android/content/res/flags.aconfig +++ b/core/java/android/content/res/flags.aconfig @@ -10,6 +10,15 @@ flag { } flag { + name: "font_scale_converter_public" + namespace: "accessibility" + description: "Enables the public API for FontScaleConverter, including enabling thread-safe caching." + bug: "239736383" + # fixed_read_only or device wont boot because of permission issues accessing flags during boot + is_fixed_read_only: true +} + +flag { name: "asset_file_descriptor_frro" namespace: "resource_manager" description: "Feature flag for passing in an AssetFileDescriptor to create an frro" diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java index 145dbf21699e..cb9c3335518a 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -17,6 +17,7 @@ package android.hardware.input; import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag; +import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag; import android.Manifest; import android.annotation.FloatRange; @@ -401,4 +402,49 @@ public class InputSettings { UserHandle.USER_CURRENT); } + /** + * Whether Accessibility sticky keys is enabled. + * + * <p> + * 'Sticky keys' is an accessibility feature that assists users who have physical + * disabilities or help users reduce repetitive strain injury. It serializes keystrokes + * instead of pressing multiple keys at a time, allowing the user to press and release a + * modifier key, such as Shift, Ctrl, Alt, or any other modifier key, and have it remain + * active until any other key is pressed. + * </p> + * + * @hide + */ + public static boolean isAccessibilityStickyKeysEnabled(@NonNull Context context) { + if (!keyboardA11yStickyKeysFlag()) { + return false; + } + return Settings.System.getIntForUser(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_STICKY_KEYS, 0, UserHandle.USER_CURRENT) != 0; + } + + /** + * Set Accessibility sticky keys feature enabled/disabled. + * + * <p> + * 'Sticky keys' is an accessibility feature that assists users who have physical + * disabilities or help users reduce repetitive strain injury. It serializes keystrokes + * instead of pressing multiple keys at a time, allowing the user to press and release a + * modifier key, such as Shift, Ctrl, Alt, or any other modifier key, and have it remain + * active until any other key is pressed. + * </p> + * + * @hide + */ + @RequiresPermission(Manifest.permission.WRITE_SETTINGS) + public static void setAccessibilityStickyKeysEnabled(@NonNull Context context, + boolean enabled) { + if (!keyboardA11yStickyKeysFlag()) { + return; + } + Settings.System.putIntForUser(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_STICKY_KEYS, enabled ? 1 : 0, + UserHandle.USER_CURRENT); + } + } diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java index 91b796aba655..77160557f5c2 100644 --- a/core/java/android/os/HidlSupport.java +++ b/core/java/android/os/HidlSupport.java @@ -218,6 +218,13 @@ public class HidlSupport { @SystemApi public static native int getPidIfSharable(); + /** + * Return true if HIDL is supported on this device and false if not. + * + * @hide + */ + public static native boolean isHidlSupported(); + /** @hide */ public HidlSupport() {} } diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index feed20800fd4..bc19655d1618 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.util.Log; import libcore.util.NativeAllocationRegistry; @@ -78,6 +79,17 @@ public abstract class HwBinder implements IHwBinder { String iface, String serviceName) throws RemoteException, NoSuchElementException { + if (!HidlSupport.isHidlSupported() + && (iface.equals("android.hidl.manager@1.0::IServiceManager") + || iface.equals("android.hidl.manager@1.1::IServiceManager") + || iface.equals("android.hidl.manager@1.2::IServiceManager"))) { + Log.i( + TAG, + "Replacing Java hwservicemanager with a fake HwNoService" + + " because HIDL is not supported on this device."); + return new HwNoService(); + } + return getService(iface, serviceName, false /* retry */); } /** diff --git a/core/java/android/os/HwNoService.java b/core/java/android/os/HwNoService.java new file mode 100644 index 000000000000..117c3ad7ee48 --- /dev/null +++ b/core/java/android/os/HwNoService.java @@ -0,0 +1,52 @@ +/* + * 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 android.os; + +/** + * A fake hwservicemanager that is used locally when HIDL isn't supported on the device. + * + * @hide + */ +final class HwNoService implements IHwBinder, IHwInterface { + /** @hide */ + @Override + public void transact(int code, HwParcel request, HwParcel reply, int flags) {} + + /** @hide */ + @Override + public IHwInterface queryLocalInterface(String descriptor) { + return new HwNoService(); + } + + /** @hide */ + @Override + public boolean linkToDeath(DeathRecipient recipient, long cookie) { + return true; + } + + /** @hide */ + @Override + public boolean unlinkToDeath(DeathRecipient recipient) { + return true; + } + + /** @hide */ + @Override + public IHwBinder asBinder() { + return this; + } +} diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 7e07e1f2e499..fc8523ee23dd 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -1599,7 +1599,7 @@ public class Process { * fully removed, otherwise system resources may leak. * @hide */ - public static final native int sendSignalToProcessGroup(int uid, int pid, int signal); + public static final native boolean sendSignalToProcessGroup(int uid, int pid, int signal); /** * Freeze the cgroup for the given UID. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2cc56d838b79..4af657dab0cd 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7330,7 +7330,7 @@ public final class Settings { "hearing_aid_media_routing"; /** - * System sounds routing value for hearing aid. It routes system sounds to hearing aid + * Notification routing value for hearing aid. It routes notification sounds to hearing aid * or device speaker. * <ul> * <li> 0 = Default @@ -7339,8 +7339,8 @@ public final class Settings { * </ul> * @hide */ - public static final String HEARING_AID_SYSTEM_SOUNDS_ROUTING = - "hearing_aid_system_sounds_routing"; + public static final String HEARING_AID_NOTIFICATION_ROUTING = + "hearing_aid_notification_routing"; /** * Setting to indicate that on device captions are enabled. @@ -7801,6 +7801,15 @@ public final class Settings { public static final String ACCESSIBILITY_BOUNCE_KEYS = "accessibility_bounce_keys"; /** + * Whether to enable sticky keys for Physical Keyboard accessibility. + * + * This is a boolean value that determines if Sticky keys feature is enabled. + * + * @hide + */ + public static final String ACCESSIBILITY_STICKY_KEYS = "accessibility_sticky_keys"; + + /** * Whether stylus button presses are disabled. This is a boolean that * determines if stylus buttons are ignored. * diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e3d9c605ff63..1712fd3c3323 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -122,7 +122,6 @@ import android.view.WindowInsets.Side.InsetsSide; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.accessibility.AccessibilityNodeInfo; -import android.window.ITrustedPresentationListener; import android.window.TaskFpsCallback; import android.window.TrustedPresentationThresholds; @@ -1280,9 +1279,8 @@ public interface WindowManager extends ViewManager { * android:value="true|false"/> * </application> * </pre> - * @hide */ - // TODO(b/279428317): Make this public API. + @FlaggedApi(Flags.FLAG_APP_COMPAT_PROPERTIES_API) String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"; diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 966161fd642a..19ba316257a3 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -45,6 +45,7 @@ import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; import android.os.Trace; +import android.service.contentcapture.ContentCaptureService; import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; @@ -69,6 +70,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -152,7 +154,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession { public ComponentName mComponentName; /** - * List of events held to be sent as a batch. + * Thread-safe queue of events held to be processed as a batch. + * + * Because it is not guaranteed that the events will be enqueued from a single thread, the + * implementation must be thread-safe to prevent unexpected behaviour. + */ + @NonNull + private final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue; + + /** + * List of events held to be sent to the {@link ContentCaptureService} as a batch. * * @hide */ @@ -238,6 +249,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null; mSessionStateReceiver = new SessionStateReceiver(this); + + mEventProcessQueue = new ConcurrentLinkedQueue<>(); } @Override @@ -733,6 +746,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } mDirectServiceInterface = null; mContentProtectionEventProcessor = null; + if (runOnBackgroundThreadEnabled()) { + mEventProcessQueue.clear(); + } } // TODO(b/122454205): once we support multiple sessions, we might need to move some of these @@ -823,27 +839,30 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // change should also get get rid of the "internalNotifyXXXX" methods above void notifyChildSessionStarted(int parentSessionId, int childSessionId, @NonNull ContentCaptureContext clientContext) { - runOnContentCaptureThread( - () -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED) - .setParentSessionId(parentSessionId).setClientContext(clientContext), - FORCE_FLUSH)); + final ContentCaptureEvent event = + new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED) + .setParentSessionId(parentSessionId) + .setClientContext(clientContext); + enqueueEvent(event, FORCE_FLUSH); } void notifyChildSessionFinished(int parentSessionId, int childSessionId) { - runOnContentCaptureThread( - () -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED) - .setParentSessionId(parentSessionId), FORCE_FLUSH)); + final ContentCaptureEvent event = + new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED) + .setParentSessionId(parentSessionId); + enqueueEvent(event, FORCE_FLUSH); } void notifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) { - runOnContentCaptureThread(() -> - sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED) - .setViewNode(node.mNode))); + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED) + .setViewNode(node.mNode); + enqueueEvent(event); } void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) { - runOnContentCaptureThread(() -> sendEvent( - new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id))); + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED) + .setAutofillId(id); + enqueueEvent(event); } void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) { @@ -867,50 +886,90 @@ public final class MainContentCaptureSession extends ContentCaptureSession { final int startIndex = Selection.getSelectionStart(text); final int endIndex = Selection.getSelectionEnd(text); - runOnContentCaptureThread(() -> sendEvent( - new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED) - .setAutofillId(id).setText(eventText) - .setComposingIndex(composingStart, composingEnd) - .setSelectionIndex(startIndex, endIndex))); + + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED) + .setAutofillId(id).setText(eventText) + .setComposingIndex(composingStart, composingEnd) + .setSelectionIndex(startIndex, endIndex); + enqueueEvent(event); } void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) { - runOnContentCaptureThread(() -> - sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED) - .setInsets(viewInsets))); + final ContentCaptureEvent event = + new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED) + .setInsets(viewInsets); + enqueueEvent(event); } void notifyViewTreeEvent(int sessionId, boolean started) { final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED; final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled(); + final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH; - runOnContentCaptureThread(() -> sendEvent( - new ContentCaptureEvent(sessionId, type), - disableFlush ? !started : FORCE_FLUSH)); + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type); + enqueueEvent(event, forceFlush); } void notifySessionResumed(int sessionId) { - runOnContentCaptureThread(() -> sendEvent( - new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED), FORCE_FLUSH)); + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED); + enqueueEvent(event, FORCE_FLUSH); } void notifySessionPaused(int sessionId) { - runOnContentCaptureThread(() -> sendEvent( - new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED), FORCE_FLUSH)); + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED); + enqueueEvent(event, FORCE_FLUSH); } void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) { - runOnContentCaptureThread(() -> - sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED) - .setClientContext(context), FORCE_FLUSH)); + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED) + .setClientContext(context); + enqueueEvent(event, FORCE_FLUSH); } /** public because is also used by ViewRootImpl */ public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) { - runOnContentCaptureThread(() -> sendEvent( + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED) - .setBounds(bounds) - )); + .setBounds(bounds); + enqueueEvent(event); + } + + private List<ContentCaptureEvent> clearBufferEvents() { + final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>(); + ContentCaptureEvent event; + while ((event = mEventProcessQueue.poll()) != null) { + bufferEvents.add(event); + } + return bufferEvents; + } + + private void enqueueEvent(@NonNull final ContentCaptureEvent event) { + enqueueEvent(event, /* forceFlush */ false); + } + + /** + * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise, + * clear the buffer events then starting sending out current event. + */ + private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) { + if (runOnBackgroundThreadEnabled()) { + if (forceFlush) { + // The buffer events are cleared in the same thread first to prevent new events + // being added during the time of context switch. This would disrupt the sequence + // of events. + final List<ContentCaptureEvent> batchEvents = clearBufferEvents(); + runOnContentCaptureThread(() -> { + for (int i = 0; i < batchEvents.size(); i++) { + sendEvent(batchEvents.get(i)); + } + sendEvent(event, /* forceFlush= */ true); + }); + } else { + mEventProcessQueue.offer(event); + } + } else { + mHandler.post(() -> sendEvent(event, forceFlush)); + } } /** public because is also used by ViewRootImpl */ diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 8ad10af7250a..0d499a1b311e 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1014,6 +1014,11 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_PENDING_INTENT_TEMPLATE_TAG; } + + @Override + public void visitUris(@NonNull Consumer<Uri> visitor) { + mPendingIntentTemplate.visitUris(visitor); + } } /** @@ -1428,9 +1433,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - // TODO(b/281044385): Maybe visit intent URIs. This may require adding a dedicated - // visitUris method in the Intent class, since it can contain other intents. Otherwise, - // the basic thing to do here would be just visitor.accept(intent.getData()). + mIntent.visitUris(visitor); } } @@ -1510,7 +1513,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse. + mResponse.visitUris(visitor); } } @@ -1559,6 +1562,11 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG; } + + @Override + public void visitUris(@NonNull Consumer<Uri> visitor) { + mPendingIntent.visitUris(visitor); + } } /** @@ -1632,7 +1640,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse. + mResponse.visitUris(visitor); } } @@ -2193,6 +2201,10 @@ public class RemoteViews implements Parcelable, Filter { final Icon icon = (Icon) getParameterValue(null); if (icon != null) visitIconUri(icon, visitor); break; + case INTENT: + final Intent intent = (Intent) getParameterValue(null); + if (intent != null) intent.visitUris(visitor); + break; // TODO(b/281044385): Should we do anything about type BUNDLE? } } @@ -6972,6 +6984,20 @@ public class RemoteViews implements Parcelable, Filter { mElementNames = parcel.createStringArrayList(); } + /** + * See {@link RemoteViews#visitUris(Consumer)}. + * + * @hide + */ + public void visitUris(@NonNull Consumer<Uri> visitor) { + if (mPendingIntent != null) { + mPendingIntent.visitUris(visitor); + } + if (mFillIntent != null) { + mFillIntent.visitUris(visitor); + } + } + private void handleViewInteraction( View v, InteractionHandler handler) { diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig index 727bff44017e..0077dab7ff63 100644 --- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig +++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig @@ -23,3 +23,16 @@ flag { bug: "297550533" is_fixed_read_only: true } + +flag { + name: "app_compat_properties_api" + namespace: "large_screen_experiences_app_compat" + description: "Whether app compat property APIs are public. Which includes: /n" + "WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE,/n" + "WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,/n" + "WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES,/n" + "WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE,/n" + "WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE" + bug: "316139088" + is_fixed_read_only: true +} diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 7e2c0179b327..86d3037b1612 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -24,7 +24,9 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROS import static android.content.ContentProvider.getUserIdFromUri; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; + import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.Animator; @@ -2868,7 +2870,6 @@ public class ChooserActivity extends ResolverActivity implements @Override public void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildComplete) { setupScrollListener(); - maybeSetupGlobalLayoutListener(); ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter; if (chooserListAdapter.getUserHandle() @@ -2970,28 +2971,6 @@ public class ChooserActivity extends ResolverActivity implements }); } - private void maybeSetupGlobalLayoutListener() { - if (shouldShowTabs()) { - return; - } - final View recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView(); - recyclerView.getViewTreeObserver() - .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - // Fixes an issue were the accessibility border disappears on list creation. - recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); - final TextView titleView = findViewById(R.id.title); - if (titleView != null) { - titleView.setFocusable(true); - titleView.setFocusableInTouchMode(true); - titleView.requestFocus(); - titleView.requestAccessibilityFocus(); - } - } - }); - } - @Override // ChooserListCommunicator public boolean isSendAction(Intent targetIntent) { if (targetIntent == null) { diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index f1aa330f562c..efc1455ecd45 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -39,7 +39,6 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.SuspendDialogInfo; -import android.content.pm.UserPackage; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -309,8 +308,7 @@ public class SuspendedAppActivity extends AlertActivity try { final String[] errored = ipm.setPackagesSuspendedAsUser( new String[]{mSuspendedPackage}, false, null, null, null, 0, - mSuspendingPackage, mUserId /* suspendingUserId */, - mUserId /* targetUserId */); + mSuspendingPackage, mUserId); if (ArrayUtils.contains(errored, mSuspendedPackage)) { Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage); break; @@ -352,18 +350,17 @@ public class SuspendedAppActivity extends AlertActivity } public static Intent createSuspendedAppInterceptIntent(String suspendedPackage, - UserPackage suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options, + String suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options, IntentSender onUnsuspend, int userId) { - Intent intent = new Intent() + return new Intent() .setClassName("android", SuspendedAppActivity.class.getName()) .putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage) .putExtra(EXTRA_DIALOG_INFO, dialogInfo) - .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage.packageName) + .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage) .putExtra(EXTRA_UNSUSPEND_INTENT, onUnsuspend) .putExtra(EXTRA_ACTIVITY_OPTIONS, options) .putExtra(Intent.EXTRA_USER_ID, userId) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - return intent; } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 8b1879f5225d..0ec8b7461221 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -117,8 +117,6 @@ public class InteractionJankMonitor { @Deprecated public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = Cuj.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; @Deprecated public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; @Deprecated public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = Cuj.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE; - @Deprecated public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME; - @Deprecated public static final int CUJ_LAUNCHER_QUICK_SWITCH = Cuj.CUJ_LAUNCHER_QUICK_SWITCH; @Deprecated public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = Cuj.CUJ_NOTIFICATION_HEADS_UP_APPEAR; @Deprecated public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = Cuj.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR; @Deprecated public static final int CUJ_NOTIFICATION_ADD = Cuj.CUJ_NOTIFICATION_ADD; diff --git a/core/jni/android_os_HidlSupport.cpp b/core/jni/android_os_HidlSupport.cpp index e3602d8f5c72..3e51e9315d89 100644 --- a/core/jni/android_os_HidlSupport.cpp +++ b/core/jni/android_os_HidlSupport.cpp @@ -15,6 +15,7 @@ */ #include <hidl/HidlTransportSupport.h> +#include <hidl/ServiceManagement.h> #include <nativehelper/JNIHelp.h> #include "core_jni_helpers.h" @@ -24,8 +25,13 @@ static jint android_os_HidlSupport_getPidIfSharable(JNIEnv*, jclass) { return android::hardware::details::getPidIfSharable(); } +static jboolean android_os_HidlSupport_isHidlSupported(JNIEnv*, jclass) { + return android::hardware::isHidlSupported(); +} + static const JNINativeMethod gHidlSupportMethods[] = { - {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable}, + {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable}, + {"isHidlSupported", "()Z", (void*)android_os_HidlSupport_isHidlSupported}, }; const char* const kHidlSupportPathName = "android/os/HidlSupport"; diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 55100a5347fd..7af69f2dff08 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -1262,7 +1262,7 @@ jint android_os_Process_killProcessGroup(JNIEnv* env, jobject clazz, jint uid, j return killProcessGroup(uid, pid, SIGKILL); } -jint android_os_Process_sendSignalToProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid, +jboolean android_os_Process_sendSignalToProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid, jint signal) { if (uid < 0) { return jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", @@ -1344,7 +1344,7 @@ static const JNINativeMethod methods[] = { //{"setApplicationObject", "(Landroid/os/IBinder;)V", //(void*)android_os_Process_setApplicationObject}, {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup}, - {"sendSignalToProcessGroup", "(III)I", (void*)android_os_Process_sendSignalToProcessGroup}, + {"sendSignalToProcessGroup", "(III)Z", (void*)android_os_Process_sendSignalToProcessGroup}, {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups}, {"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen}, {"freezeCgroupUid", "(IZ)V", (void*)android_os_Process_freezeCgroupUID}, diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 9ca1849dedc2..104c023f550d 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -93,7 +93,7 @@ message SecureSettingsProto { optional SettingProto hearing_aid_ringtone_routing = 46 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto hearing_aid_call_routing = 47 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto hearing_aid_media_routing = 48 [ (android.privacy).dest = DEST_AUTOMATIC ]; - optional SettingProto hearing_aid_system_sounds_routing = 49 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hearing_aid_notification_routing = 49 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_magnification_joystick_enabled = 50 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Settings for font scaling optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 404fa39fb6ed..382a82cd090e 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -394,6 +394,7 @@ message ActivityRecordProto { optional bool should_force_rotate_for_camera_compat = 39; optional bool should_refresh_activity_for_camera_compat = 40; optional bool should_refresh_activity_via_pause_for_camera_compat = 41; + optional bool should_override_min_aspect_ratio = 42; } /* represents WindowToken */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0264fdc198f3..c6209dd25c47 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5189,7 +5189,9 @@ this permission, it must hold the permission and be the active VoiceInteractionService in the system. {@see Settings.Secure.VOICE_INTERACTION_SERVICE} - @hide --> + @hide @SystemApi Intended for OEM and system apps. + <p>Protection level: signature|privileged + --> <permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES" android:protectionLevel="signature|privileged" /> @@ -5198,7 +5200,9 @@ sound models at any time. This permission should be reserved for system enrollment applications detected by {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo} only. - @hide <p>Not for use by third-party applications.</p> --> + @hide @SystemApi Intended for OEM and system apps. + <p>Protection level: signature|privileged + --> <permission android:name="android.permission.KEYPHRASE_ENROLLMENT_APPLICATION" android:protectionLevel="signature|privileged" /> @@ -5702,11 +5706,10 @@ android:protectionLevel="normal" /> <!-- @hide @FlaggedApi("android.companion.flags.companion_transport_apis") - @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES) Allows an application to send and receive messages via CDM transports. --> <permission android:name="android.permission.USE_COMPANION_TRANSPORTS" - android:protectionLevel="signature|module" /> + android:protectionLevel="signature" /> <!-- Allows an application to create new companion device associations. @SystemApi diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt index ba6c8fab48d4..8308e7c5ef19 100644 --- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt @@ -16,24 +16,35 @@ package android.content.res - import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.core.util.forEach import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage +import org.junit.Rule import kotlin.math.ceil import kotlin.math.floor import org.junit.Test import org.junit.runner.RunWith +import java.lang.IllegalStateException import kotlin.random.Random.Default.nextFloat +/** + * Unit tests for FontScaleConverterFactory. Note that some similar tests are in + * cts/tests/tests/content/src/android/content/res/cts/FontScaleConverterFactoryTest.kt + */ @Presubmit @RunWith(AndroidJUnit4::class) class FontScaleConverterFactoryTest { + @get:Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Test fun scale200IsTwiceAtSmallSizes() { val table = FontScaleConverterFactory.forScale(2F)!! @@ -61,19 +72,6 @@ class FontScaleConverterFactoryTest { assertThat(table.convertSpToDp(100F)).isWithin(CONVERSION_TOLERANCE).of(300f) } - @SmallTest - fun missingLookupTable110_returnsInterpolated() { - val table = FontScaleConverterFactory.forScale(1.1F)!! - - assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1.1f) - assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.1f) - assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(11f) - assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.1f) - assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) - assertThat(table.convertSpToDp(50F)).isLessThan(50f * 1.1f) - assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.1f) - } - @Test fun missingLookupTable199_returnsInterpolated() { val table = FontScaleConverterFactory.forScale(1.9999F)!! @@ -96,21 +94,51 @@ class FontScaleConverterFactoryTest { assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) } + @Test + @RequiresFlagsEnabled(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) + fun missingLookupTable_cachesInterpolated() { + val table = FontScaleConverterFactory.forScale(1.6F)!! + + assertThat(FontScaleConverterFactory.sLookupTables.contains((1.6F * 100).toInt())).isTrue() + // Double check known existing values + assertThat(FontScaleConverterFactory.sLookupTables.contains((1.5F * 100).toInt())).isTrue() + assertThat(FontScaleConverterFactory.sLookupTables.contains((1.7F * 100).toInt())).isFalse() + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) + fun missingLookupTablePastEnd_cachesLinear() { + val table = FontScaleConverterFactory.forScale(3F)!! + + assertThat(FontScaleConverterFactory.sLookupTables.contains((3F * 100).toInt())).isTrue() + // Double check known existing values + assertThat(FontScaleConverterFactory.sLookupTables.contains((1.5F * 100).toInt())).isTrue() + assertThat(FontScaleConverterFactory.sLookupTables.contains((1.7F * 100).toInt())).isFalse() + } + @SmallTest + @Test fun missingLookupTableNegativeReturnsNull() { assertThat(FontScaleConverterFactory.forScale(-1F)).isNull() } @SmallTest + @Test fun unnecessaryFontScalesReturnsNull() { assertThat(FontScaleConverterFactory.forScale(0F)).isNull() assertThat(FontScaleConverterFactory.forScale(1F)).isNull() + assertThat(FontScaleConverterFactory.forScale(1.1F)).isNull() assertThat(FontScaleConverterFactory.forScale(0.85F)).isNull() } @SmallTest + @Test fun tablesMatchAndAreMonotonicallyIncreasing() { - FontScaleConverterFactory.LOOKUP_TABLES.forEach { _, lookupTable -> + FontScaleConverterFactory.sLookupTables.forEach { _, lookupTable -> + if (lookupTable !is FontScaleConverterImpl) { + throw IllegalStateException("Didn't return a FontScaleConverterImpl") + } + assertThat(lookupTable.mToDpValues).hasLength(lookupTable.mFromSpValues.size) assertThat(lookupTable.mToDpValues).isNotEmpty() @@ -123,6 +151,7 @@ class FontScaleConverterFactoryTest { } @SmallTest + @Test fun testIsNonLinearFontScalingActive() { assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1f)).isFalse() assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0f)).isFalse() diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt index bfa8c9ada911..2c614424a9a5 100644 --- a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt @@ -86,13 +86,13 @@ class FontScaleConverterTest { } private fun createTable(vararg pairs: Pair<Float, Float>) = - FontScaleConverter( + FontScaleConverterImpl( pairs.map { it.first }.toFloatArray(), pairs.map { it.second }.toFloatArray() ) private fun verifyConversionBothWays( - table: FontScaleConverter, + table: FontScaleConverterImpl, expectedDp: Float, spToConvert: Float ) { diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index c8ea3742b3aa..15c90474c017 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -34,9 +34,6 @@ import android.app.PendingIntent; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; @@ -837,6 +834,33 @@ public class RemoteViewsTest { } @Test + public void visitUris_intents() { + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); + + Uri fillIntentUri = Uri.parse("content://intent/fill"); + views.setOnCheckedChangeResponse( + R.id.layout, + RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri))); + + Uri pendingIntentUri = Uri.parse("content://intent/pending"); + PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri); + views.setOnClickResponse( + R.id.layout, + RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent)); + + Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); + views.visitUris(visitor); + verify(visitor, times(1)).accept(eq(fillIntentUri)); + verify(visitor, times(1)).accept(eq(pendingIntentUri)); + } + + private PendingIntent getPendingIntentWithUri(Uri uri) { + return PendingIntent.getActivity(mContext, 0, + new Intent("action", uri), + PendingIntent.FLAG_IMMUTABLE); + } + + @Test public void layoutInflaterFactory_nothingSet_returnsNull() { final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); assertNull(rv.getLayoutInflaterFactory()); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 3e7f99b96421..ed8c4f31306b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -344,7 +344,9 @@ class TaskFragmentContainer { if (activities == null) { return null; } - return new ActivityStack(activities, isEmpty(), mToken); + // Already checked nullity in collectNonFinishingActivities. + final Rect bounds = getInfo().getConfiguration().windowConfiguration.getBounds(); + return new ActivityStack(activities, isEmpty(), mToken, bounds, mOverlayTag); } /** Adds the activity that will be reparented to this container. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEducationController.kt index e57f02c71e44..bd4708259b50 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEducationController.kt @@ -40,7 +40,13 @@ class BubbleEducationController(private val context: Context) { /** Whether education view should show for the collapsed stack. */ fun shouldShowStackEducation(bubble: BubbleViewProvider?): Boolean { - val shouldShow = bubble != null && + if (BubbleDebugConfig.neverShowUserEducation(context)) { + logDebug("Show stack edu: never") + return false + } + + val shouldShow = + bubble != null && bubble.isConversationBubble && // show education for conversation bubbles only (!hasSeenStackEducation || BubbleDebugConfig.forceShowUserEducation(context)) logDebug("Show stack edu: $shouldShow") @@ -49,7 +55,13 @@ class BubbleEducationController(private val context: Context) { /** Whether the educational view should show for the expanded view "manage" menu. */ fun shouldShowManageEducation(bubble: BubbleViewProvider?): Boolean { - val shouldShow = bubble != null && + if (BubbleDebugConfig.neverShowUserEducation(context)) { + logDebug("Show manage edu: never") + return false + } + + val shouldShow = + bubble != null && bubble.isConversationBubble && // show education for conversation bubbles only (!hasSeenManageEducation || BubbleDebugConfig.forceShowUserEducation(context)) logDebug("Show manage edu: $shouldShow") diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 7f34ee0cdd3d..f794fef48f27 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -23,6 +23,8 @@ import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Point; import android.util.Log; +import android.util.Size; +import android.view.View; import android.widget.FrameLayout; import androidx.annotation.Nullable; @@ -33,6 +35,7 @@ import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleViewProvider; import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix; +import com.android.wm.shell.common.magnetictarget.MagnetizedObject.MagneticTarget; /** * Helper class to animate a {@link BubbleBarExpandedView} on a bubble. @@ -44,6 +47,13 @@ public class BubbleBarAnimationHelper { private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f; private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f; private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150; + private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100; + /** + * Additional scale applied to expanded view when it is positioned inside a magnetic target. + */ + private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f; + private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300; + private static final int EXPANDED_VIEW_DISMISS_DURATION = 250; /** Spring config for the expanded view scale-in animation. */ private final PhysicsAnimator.SpringConfig mScaleInSpringConfig = @@ -181,7 +191,8 @@ public class BubbleBarAnimationHelper { Log.w(TAG, "Trying to animate collapse without a bubble"); return; } - + bbev.setScaleX(1f); + bbev.setScaleY(1f); mExpandedViewContainerMatrix.setScaleX(1f); mExpandedViewContainerMatrix.setScaleY(1f); @@ -209,11 +220,124 @@ public class BubbleBarAnimationHelper { } /** + * Animates dismissal of currently expanded bubble + * + * @param endRunnable a runnable to run at the end of the animation + */ + public void animateDismiss(Runnable endRunnable) { + mIsExpanded = false; + final BubbleBarExpandedView bbev = getExpandedView(); + if (bbev == null) { + Log.w(TAG, "Trying to animate dismiss without a bubble"); + return; + } + + int[] location = bbev.getLocationOnScreen(); + int diffFromBottom = mPositioner.getScreenRect().bottom - location[1]; + + bbev.animate() + // 2x distance from bottom so the view flies out + .translationYBy(diffFromBottom * 2) + .setDuration(EXPANDED_VIEW_DISMISS_DURATION) + .withEndAction(endRunnable) + .start(); + } + + /** + * Animate current expanded bubble back to its rest position + */ + public void animateToRestPosition() { + BubbleBarExpandedView bbev = getExpandedView(); + if (bbev == null) { + Log.w(TAG, "Trying to animate expanded view to rest position without a bubble"); + return; + } + Point restPoint = getExpandedViewRestPosition(getExpandedViewSize()); + bbev.animate() + .x(restPoint.x) + .y(restPoint.y) + .scaleX(1f) + .scaleY(1f) + .setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION) + .setInterpolator(Interpolators.EMPHASIZED_DECELERATE) + .withStartAction(() -> bbev.setAnimating(true)) + .withEndAction(() -> bbev.setAnimating(false)) + .start(); + } + + /** + * Animates currently expanded bubble into the given {@link MagneticTarget}. + * + * @param target magnetic target to snap to + * @param endRunnable a runnable to run at the end of the animation + */ + public void animateIntoTarget(MagneticTarget target, @Nullable Runnable endRunnable) { + BubbleBarExpandedView bbev = getExpandedView(); + if (bbev == null) { + Log.w(TAG, "Trying to snap the expanded view to target without a bubble"); + return; + } + Point expandedViewCenter = getViewCenterOnScreen(bbev); + + // Calculate the difference between the target's center coordinates and the object's. + // Animating the object's x/y properties by these values will center the object on top + // of the magnetic target. + float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x; + float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y; + + // Calculate scale of expanded view so it fits inside the magnetic target + float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight()); + float targetMaxSide = Math.max(target.getTargetView().getWidth(), + target.getTargetView().getHeight()); + float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide; + + bbev.animate() + .translationX(bbev.getTranslationX() + xDiff) + .translationY(bbev.getTranslationY() + yDiff) + .scaleX(scale) + .scaleY(scale) + .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION) + .setInterpolator(Interpolators.EMPHASIZED) + .withStartAction(() -> bbev.setAnimating(true)) + .withEndAction(() -> { + bbev.setAnimating(false); + if (endRunnable != null) { + endRunnable.run(); + } + }) + .start(); + } + + /** + * Animate currently expanded view when it is released from dismiss view + */ + public void animateUnstuckFromDismissView() { + BubbleBarExpandedView expandedView = getExpandedView(); + if (expandedView == null) { + Log.w(TAG, "Trying to unsnap the expanded view from dismiss without a bubble"); + return; + } + expandedView + .animate() + .scaleX(1f) + .scaleY(1f) + .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION) + .setInterpolator(Interpolators.EMPHASIZED) + .withStartAction(() -> expandedView.setAnimating(true)) + .withEndAction(() -> expandedView.setAnimating(false)) + .start(); + } + + /** * Cancel current animations */ public void cancelAnimations() { PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); mExpandedViewAlphaAnimator.cancel(); + BubbleBarExpandedView bbev = getExpandedView(); + if (bbev != null) { + bbev.animate().cancel(); + } } private @Nullable BubbleBarExpandedView getExpandedView() { @@ -231,21 +355,42 @@ public class BubbleBarAnimationHelper { return; } - boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY); - final int padding = mPositioner.getBubbleBarExpandedViewPadding(); - final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded); - final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded); + final Size size = getExpandedViewSize(); + Point position = getExpandedViewRestPosition(size); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bbev.getLayoutParams(); - lp.width = width; - lp.height = height; + lp.width = size.getWidth(); + lp.height = size.getHeight(); bbev.setLayoutParams(lp); + bbev.setX(position.x); + bbev.setY(position.y); + bbev.updateLocation(); + bbev.maybeShowOverflow(); + } + + private Point getExpandedViewRestPosition(Size size) { + final int padding = mPositioner.getBubbleBarExpandedViewPadding(); + Point point = new Point(); if (mLayerView.isOnLeft()) { - bbev.setX(mPositioner.getInsets().left + padding); + point.x = mPositioner.getInsets().left + padding; } else { - bbev.setX(mPositioner.getAvailableRect().width() - width - padding); + point.x = mPositioner.getAvailableRect().width() - size.getWidth() - padding; } - bbev.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height); - bbev.updateLocation(); - bbev.maybeShowOverflow(); + point.y = mPositioner.getExpandedViewBottomForBubbleBar() - size.getHeight(); + return point; + } + + private Size getExpandedViewSize() { + boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY); + final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded); + final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded); + return new Size(width, height); + } + + private Point getViewCenterOnScreen(View view) { + Point center = new Point(); + int[] onScreenLocation = view.getLocationOnScreen(); + center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f)); + center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f)); + return center; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt index 4ea18f78f5b2..d21545079cc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt @@ -16,70 +16,67 @@ package com.android.wm.shell.bubbles.bar -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.graphics.PointF -import android.graphics.Rect +import android.annotation.SuppressLint import android.view.MotionEvent import android.view.View -import com.android.wm.shell.animation.Interpolators import com.android.wm.shell.common.bubbles.DismissView import com.android.wm.shell.common.bubbles.RelativeTouchListener +import com.android.wm.shell.common.magnetictarget.MagnetizedObject /** Controller for handling drag interactions with [BubbleBarExpandedView] */ +@SuppressLint("ClickableViewAccessibility") class BubbleBarExpandedViewDragController( private val expandedView: BubbleBarExpandedView, private val dismissView: DismissView, + private val animationHelper: BubbleBarAnimationHelper, private val onDismissed: () -> Unit ) { + var isStuckToDismiss: Boolean = false + private set + + private var expandedViewInitialTranslationX = 0f + private var expandedViewInitialTranslationY = 0f + private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> = + MagnetizedObject.magnetizeView(expandedView) + private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget + init { - expandedView.handleView.setOnTouchListener(HandleDragListener()) - } + magnetizedExpandedView.magnetListener = MagnetListener() + magnetizedExpandedView.animateStuckToTarget = + { + target: MagnetizedObject.MagneticTarget, + _: Float, + _: Float, + _: Boolean, + after: (() -> Unit)? -> + animationHelper.animateIntoTarget(target, after) + } - private fun finishDrag(x: Float, y: Float, viewInitialX: Float, viewInitialY: Float) { - val dismissCircleBounds = Rect().apply { dismissView.circle.getBoundsOnScreen(this) } - if (dismissCircleBounds.contains(x.toInt(), y.toInt())) { - onDismissed() - } else { - resetExpandedViewPosition(viewInitialX, viewInitialY) - } - dismissView.hide() - } + magnetizedDismissTarget = + MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width) + magnetizedExpandedView.addTarget(magnetizedDismissTarget) - private fun resetExpandedViewPosition(initialX: Float, initialY: Float) { - val listener = - object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator) { - expandedView.isAnimating = true - } + val dragMotionEventHandler = HandleDragListener() - override fun onAnimationEnd(animation: Animator) { - expandedView.isAnimating = false - } + expandedView.handleView.setOnTouchListener { view, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + expandedViewInitialTranslationX = expandedView.translationX + expandedViewInitialTranslationY = expandedView.translationY } - expandedView - .animate() - .translationX(initialX) - .translationY(initialY) - .setDuration(RESET_POSITION_ANIM_DURATION) - .setInterpolator(Interpolators.EMPHASIZED_DECELERATE) - .setListener(listener) - .start() + val magnetConsumed = magnetizedExpandedView.maybeConsumeMotionEvent(event) + // Move events can be consumed by the magnetized object + if (event.actionMasked == MotionEvent.ACTION_MOVE && magnetConsumed) { + return@setOnTouchListener true + } + return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed + } } private inner class HandleDragListener : RelativeTouchListener() { - - private val expandedViewRestPosition = PointF() - override fun onDown(v: View, ev: MotionEvent): Boolean { // While animating, don't allow new touch events - if (expandedView.isAnimating) { - return false - } - expandedViewRestPosition.x = expandedView.translationX - expandedViewRestPosition.y = expandedView.translationY - return true + return !expandedView.isAnimating } override fun onMove( @@ -90,8 +87,8 @@ class BubbleBarExpandedViewDragController( dx: Float, dy: Float ) { - expandedView.translationX = expandedViewRestPosition.x + dx - expandedView.translationY = expandedViewRestPosition.y + dy + expandedView.translationX = expandedViewInitialTranslationX + dx + expandedView.translationY = expandedViewInitialTranslationY + dy dismissView.show() } @@ -105,16 +102,40 @@ class BubbleBarExpandedViewDragController( velX: Float, velY: Float ) { - finishDrag(ev.rawX, ev.rawY, expandedViewRestPosition.x, expandedViewRestPosition.y) + finishDrag() } override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) { - resetExpandedViewPosition(expandedViewRestPosition.x, expandedViewRestPosition.y) - dismissView.hide() + finishDrag() + } + + private fun finishDrag() { + if (!isStuckToDismiss) { + animationHelper.animateToRestPosition() + dismissView.hide() + } } } - companion object { - const val RESET_POSITION_ANIM_DURATION = 300L + private inner class MagnetListener : MagnetizedObject.MagnetListener { + override fun onStuckToTarget(target: MagnetizedObject.MagneticTarget) { + isStuckToDismiss = true + } + + override fun onUnstuckFromTarget( + target: MagnetizedObject.MagneticTarget, + velX: Float, + velY: Float, + wasFlungOut: Boolean + ) { + isStuckToDismiss = false + animationHelper.animateUnstuckFromDismissView() + } + + override fun onReleasedInTarget(target: MagnetizedObject.MagneticTarget) { + onDismissed() + dismissView.hide() + } } } + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index bdb0e206e490..12114519d086 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles.bar; import static com.android.wm.shell.animation.Interpolators.ALPHA_IN; import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT; +import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE; import android.annotation.Nullable; import android.content.Context; @@ -36,7 +37,6 @@ import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleViewProvider; -import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.bubbles.DeviceConfig; import com.android.wm.shell.bubbles.DismissViewUtils; import com.android.wm.shell.common.bubbles.DismissView; @@ -206,10 +206,13 @@ public class BubbleBarLayerView extends FrameLayout } }); - mDragController = new BubbleBarExpandedViewDragController(mExpandedView, mDismissView, + mDragController = new BubbleBarExpandedViewDragController( + mExpandedView, + mDismissView, + mAnimationHelper, () -> { mBubbleController.dismissBubble(mExpandedBubble.getKey(), - Bubbles.DISMISS_USER_GESTURE); + DISMISS_USER_GESTURE); return Unit.INSTANCE; }); @@ -241,7 +244,11 @@ public class BubbleBarLayerView extends FrameLayout mIsExpanded = false; final BubbleBarExpandedView viewToRemove = mExpandedView; mEducationViewController.hideEducation(/* animated = */ true); - mAnimationHelper.animateCollapse(() -> removeView(viewToRemove)); + if (mDragController != null && mDragController.isStuckToDismiss()) { + mAnimationHelper.animateDismiss(() -> removeView(viewToRemove)); + } else { + mAnimationHelper.animateCollapse(() -> removeView(viewToRemove)); + } mBubbleController.getSysuiProxy().onStackExpandChanged(false); mExpandedView = null; mDragController = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index 0448d94669ce..0b8f60e44c7e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -96,7 +96,7 @@ public class PipScheduler { @Nullable private WindowContainerTransaction getExitPipViaExpandTransaction() { - if (mPipTaskToken == null || mPinnedTaskLeash == null) { + if (mPipTaskToken == null) { return null; } WindowContainerTransaction wct = new WindowContainerTransaction(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index 6200ea583a48..48a0a46dccc1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -18,6 +18,7 @@ package com.android.wm.shell.pip2.phone; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_PIP; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; @@ -56,6 +57,8 @@ public class PipTransition extends PipTransitionController { private IBinder mAutoEnterButtonNavTransition; @Nullable private IBinder mExitViaExpandTransition; + @Nullable + private IBinder mLegacyEnterTransition; public PipTransition( @NonNull ShellInit shellInit, @@ -98,6 +101,9 @@ public class PipTransition extends PipTransitionController { if (isAutoEnterInButtonNavigation(request)) { mAutoEnterButtonNavTransition = transition; return getEnterPipTransaction(transition, request); + } else if (isLegacyEnter(request)) { + mLegacyEnterTransition = transition; + return getEnterPipTransaction(transition, request); } return null; } @@ -108,6 +114,9 @@ public class PipTransition extends PipTransitionController { if (isAutoEnterInButtonNavigation(request)) { outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */); mAutoEnterButtonNavTransition = transition; + } else if (isLegacyEnter(request)) { + outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */); + mLegacyEnterTransition = transition; } } @@ -153,6 +162,10 @@ public class PipTransition extends PipTransitionController { && pipTask.pictureInPictureParams.isAutoEnterEnabled(); } + private boolean isLegacyEnter(@NonNull TransitionRequestInfo requestInfo) { + return requestInfo.getType() == TRANSIT_PIP; + } + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @@ -161,29 +174,65 @@ public class PipTransition extends PipTransitionController { @NonNull Transitions.TransitionFinishCallback finishCallback) { if (transition == mAutoEnterButtonNavTransition) { mAutoEnterButtonNavTransition = null; - TransitionInfo.Change pipChange = getPipChange(info); - if (pipChange == null) { - return false; - } - mPipTaskToken = pipChange.getContainer(); - - // cache the PiP task token and leash - mPipScheduler.setPipTaskToken(mPipTaskToken); - mPipScheduler.setPinnedTaskLeash(pipChange.getLeash()); - - startTransaction.apply(); - finishCallback.onTransitionFinished(null); - return true; + return startAutoEnterButtonNavAnimation(info, startTransaction, finishTransaction, + finishCallback); + } else if (transition == mLegacyEnterTransition) { + mLegacyEnterTransition = null; + return startLegacyEnterAnimation(info, startTransaction, finishTransaction, + finishCallback); } else if (transition == mExitViaExpandTransition) { mExitViaExpandTransition = null; - startTransaction.apply(); - finishCallback.onTransitionFinished(null); - onExitPip(); - return true; + return startExpandAnimation(info, startTransaction, finishTransaction, finishCallback); } return false; } + private boolean startAutoEnterButtonNavAnimation(@NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + TransitionInfo.Change pipChange = getPipChange(info); + if (pipChange == null) { + return false; + } + mPipTaskToken = pipChange.getContainer(); + + // cache the PiP task token and leash + mPipScheduler.setPipTaskToken(mPipTaskToken); + + startTransaction.apply(); + finishCallback.onTransitionFinished(null); + return true; + } + + private boolean startLegacyEnterAnimation(@NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + TransitionInfo.Change pipChange = getPipChange(info); + if (pipChange == null) { + return false; + } + mPipTaskToken = pipChange.getContainer(); + + // cache the PiP task token and leash + mPipScheduler.setPipTaskToken(mPipTaskToken); + + startTransaction.apply(); + finishCallback.onTransitionFinished(null); + return true; + } + + private boolean startExpandAnimation(@NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + startTransaction.apply(); + finishCallback.onTransitionFinished(null); + onExitPip(); + return true; + } + @Nullable private TransitionInfo.Change getPipChange(TransitionInfo info) { for (TransitionInfo.Change change : info.getChanges()) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java index d8475b3f22af..ca0cad759a08 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java @@ -39,21 +39,22 @@ public final class HearingAidAudioRoutingConstants { public static final int[] MEDIA_ROUTING_ATTRIBUTES = new int[] { // Stands for STRATEGY_MEDIA, including USAGE_GAME, USAGE_ASSISTANT, // USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, USAGE_ASSISTANCE_SONIFICATION - AudioAttributes.USAGE_MEDIA + AudioAttributes.USAGE_MEDIA, + // Stands for STRATEGY_ACCESSIBILITY + AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY, + // Stands for STRATEGY_DTMF + AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING, }; - public static final int[] RINGTONE_ROUTING_ATTRIBUTE = new int[] { + public static final int[] RINGTONE_ROUTING_ATTRIBUTES = new int[] { // Stands for STRATEGY_SONIFICATION, including USAGE_ALARM AudioAttributes.USAGE_NOTIFICATION_RINGTONE }; - public static final int[] SYSTEM_SOUNDS_ROUTING_ATTRIBUTES = new int[] { + public static final int[] NOTIFICATION_ROUTING_ATTRIBUTES = new int[] { // Stands for STRATEGY_SONIFICATION_RESPECTFUL, including USAGE_NOTIFICATION_EVENT AudioAttributes.USAGE_NOTIFICATION, - // Stands for STRATEGY_ACCESSIBILITY - AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY, - // Stands for STRATEGY_DTMF - AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING, + }; @Retention(RetentionPolicy.SOURCE) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java index 111a2c1a07ea..3a15b7108e3f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java @@ -26,6 +26,7 @@ import android.media.AudioDeviceAttributes; import android.media.audiopolicy.AudioProductStrategy; import android.os.ParcelUuid; import android.provider.Settings; +import android.util.FeatureFlagUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -42,11 +43,13 @@ public class HearingAidDeviceManager { private static final boolean DEBUG = BluetoothUtils.D; private final ContentResolver mContentResolver; + private final Context mContext; private final LocalBluetoothManager mBtManager; private final List<CachedBluetoothDevice> mCachedDevices; private final HearingAidAudioRoutingHelper mRoutingHelper; HearingAidDeviceManager(Context context, LocalBluetoothManager localBtManager, List<CachedBluetoothDevice> CachedDevices) { + mContext = context; mContentResolver = context.getContentResolver(); mBtManager = localBtManager; mCachedDevices = CachedDevices; @@ -56,6 +59,7 @@ public class HearingAidDeviceManager { @VisibleForTesting HearingAidDeviceManager(Context context, LocalBluetoothManager localBtManager, List<CachedBluetoothDevice> cachedDevices, HearingAidAudioRoutingHelper routingHelper) { + mContext = context; mContentResolver = context.getContentResolver(); mBtManager = localBtManager; mCachedDevices = cachedDevices; @@ -285,11 +289,13 @@ public class HearingAidDeviceManager { } void onActiveDeviceChanged(CachedBluetoothDevice device) { - if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice( - BluetoothProfile.LE_AUDIO)) { - setAudioRoutingConfig(device); - } else { - clearAudioRoutingConfig(); + if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) { + if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice( + BluetoothProfile.LE_AUDIO)) { + setAudioRoutingConfig(device); + } else { + clearAudioRoutingConfig(); + } } } @@ -312,7 +318,7 @@ public class HearingAidDeviceManager { Settings.Secure.HEARING_AID_RINGTONE_ROUTING, HearingAidAudioRoutingConstants.RoutingValue.AUTO); final int systemSoundsRoutingValue = Settings.Secure.getInt(mContentResolver, - Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING, + Settings.Secure.HEARING_AID_NOTIFICATION_ROUTING, HearingAidAudioRoutingConstants.RoutingValue.AUTO); setPreferredDeviceRoutingStrategies( @@ -322,10 +328,10 @@ public class HearingAidDeviceManager { HearingAidAudioRoutingConstants.MEDIA_ROUTING_ATTRIBUTES, hearingDeviceAttributes, mediaRoutingValue); setPreferredDeviceRoutingStrategies( - HearingAidAudioRoutingConstants.RINGTONE_ROUTING_ATTRIBUTE, + HearingAidAudioRoutingConstants.RINGTONE_ROUTING_ATTRIBUTES, hearingDeviceAttributes, ringtoneRoutingValue); setPreferredDeviceRoutingStrategies( - HearingAidAudioRoutingConstants.SYSTEM_SOUNDS_ROUTING_ATTRIBUTES, + HearingAidAudioRoutingConstants.NOTIFICATION_ROUTING_ATTRIBUTES, hearingDeviceAttributes, systemSoundsRoutingValue); } @@ -338,10 +344,10 @@ public class HearingAidDeviceManager { HearingAidAudioRoutingConstants.MEDIA_ROUTING_ATTRIBUTES, /* hearingDevice = */ null, HearingAidAudioRoutingConstants.RoutingValue.AUTO); setPreferredDeviceRoutingStrategies( - HearingAidAudioRoutingConstants.RINGTONE_ROUTING_ATTRIBUTE, + HearingAidAudioRoutingConstants.RINGTONE_ROUTING_ATTRIBUTES, /* hearingDevice = */ null, HearingAidAudioRoutingConstants.RoutingValue.AUTO); setPreferredDeviceRoutingStrategies( - HearingAidAudioRoutingConstants.SYSTEM_SOUNDS_ROUTING_ATTRIBUTES, + HearingAidAudioRoutingConstants.NOTIFICATION_ROUTING_ATTRIBUTES, /* hearingDevice = */ null, HearingAidAudioRoutingConstants.RoutingValue.AUTO); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index ea10944be0e9..24fd06e51418 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -42,6 +42,7 @@ import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.audiopolicy.AudioProductStrategy; import android.os.Parcel; +import android.util.FeatureFlagUtils; import androidx.test.core.app.ApplicationProvider; @@ -109,6 +110,7 @@ public class HearingAidDeviceManagerTest { @Before public void setUp() { + FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, true); when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1); when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2); when(mDevice1.getName()).thenReturn(DEVICE_NAME_1); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index ec456e09a3c1..5004f251cfd3 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -74,6 +74,7 @@ public class SecureSettings { Settings.Secure.TTS_DEFAULT_LOCALE, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS, + Settings.Secure.ACCESSIBILITY_STICKY_KEYS, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, // moved to global Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, // moved to global Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT, // moved to global @@ -246,7 +247,7 @@ public class SecureSettings { Settings.Secure.HEARING_AID_RINGTONE_ROUTING, Settings.Secure.HEARING_AID_CALL_ROUTING, Settings.Secure.HEARING_AID_MEDIA_ROUTING, - Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING, + Settings.Secure.HEARING_AID_NOTIFICATION_ROUTING, Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED, Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 5ad14ceb31cf..0b0e182746e5 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -120,6 +120,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.TTS_DEFAULT_LOCALE, TTS_LIST_VALIDATOR); VALIDATORS.put(Secure.SHOW_IME_WITH_HARD_KEYBOARD, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_BOUNCE_KEYS, ANY_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.ACCESSIBILITY_STICKY_KEYS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.WIFI_NUM_OPEN_NETWORKS_KEPT, NON_NEGATIVE_INTEGER_VALIDATOR); @@ -395,7 +396,7 @@ public class SecureSettingsValidators { new DiscreteValueValidator(new String[] {"0", "1", "2"})); VALIDATORS.put(Secure.HEARING_AID_MEDIA_ROUTING, new DiscreteValueValidator(new String[] {"0", "1", "2"})); - VALIDATORS.put(Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING, + VALIDATORS.put(Secure.HEARING_AID_NOTIFICATION_ROUTING, new DiscreteValueValidator(new String[] {"0", "1", "2"})); VALIDATORS.put(Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 5afcd5db1b5f..1670a705f939 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1866,8 +1866,8 @@ class SettingsProtoDumpUtil { Settings.Secure.HEARING_AID_MEDIA_ROUTING, SecureSettingsProto.Accessibility.HEARING_AID_MEDIA_ROUTING); dumpSetting(s, p, - Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING, - SecureSettingsProto.Accessibility.HEARING_AID_SYSTEM_SOUNDS_ROUTING); + Settings.Secure.HEARING_AID_NOTIFICATION_ROUTING, + SecureSettingsProto.Accessibility.HEARING_AID_NOTIFICATION_ROUTING); dumpSetting(s, p, Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, SecureSettingsProto.Accessibility.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index f10ac1bf1e0a..cc5dfc66cb74 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -277,6 +277,7 @@ android_library { "SystemUIPluginLib", "SystemUISharedLib", "SystemUICustomizationLib", + "SystemUICustomizationTestUtils", "SystemUI-statsd", "SettingsLib", "com_android_systemui_flags_lib", diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index facb244a5489..21ca613bca34 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -160,6 +160,15 @@ flag { } flag { + name: "edge_back_gesture_handler_thread" + namespace: "systemui" + description: "Moves the EdgeBackGestureHandler window, which is used for rendering the back " + "arrow, to a separate thread. Previously, the EdgeBackGestureHandler window would share " + "the main thread with the rest of System UI." + bug: "304583132" +} + +flag { name: "new_aod_transition" namespace: "systemui" description: "New LOCKSCREEN <=> AOD transition" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index d83f3aae1ace..249b3e14ec72 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -74,7 +74,7 @@ fun CommunalContainer( ) { val currentScene: SceneKey by viewModel.currentScene - .transform<CommunalSceneKey, SceneKey> { value -> value.toTransitionSceneKey() } + .transform { value -> emit(value.toTransitionSceneKey()) } .collectAsState(TransitionSceneKey.Blank) val sceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) } // Don't show hub mode UI if keyguard is present. This is important since we're in the shade, diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt new file mode 100644 index 000000000000..0b7c3f987db2 --- /dev/null +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt @@ -0,0 +1,73 @@ +/* + * 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.shared.notifications.data.repository + +import android.provider.Settings +import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel +import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.withContext + +/** Provides access to state related to notifications. */ +class NotificationSettingsRepository( + scope: CoroutineScope, + private val backgroundDispatcher: CoroutineDispatcher, + private val secureSettingsRepository: SecureSettingsRepository, +) { + /** The current state of the notification setting. */ + val settings: SharedFlow<NotificationSettingsModel> = + secureSettingsRepository + .intSetting( + name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, + ) + .map { lockScreenShowNotificationsInt -> + NotificationSettingsModel( + isShowNotificationsOnLockScreenEnabled = lockScreenShowNotificationsInt == 1, + ) + } + .shareIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + replay = 1, + ) + + suspend fun getSettings(): NotificationSettingsModel { + return withContext(backgroundDispatcher) { + NotificationSettingsModel( + isShowNotificationsOnLockScreenEnabled = + secureSettingsRepository.get( + name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, + defaultValue = 0, + ) == 1 + ) + } + } + + suspend fun setSettings(model: NotificationSettingsModel) { + withContext(backgroundDispatcher) { + secureSettingsRepository.set( + name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, + value = if (model.isShowNotificationsOnLockScreenEnabled) 1 else 0, + ) + } + } +} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt new file mode 100644 index 000000000000..21f3acaf15ff --- /dev/null +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt @@ -0,0 +1,48 @@ +/* + * 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.shared.notifications.domain.interactor + +import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository +import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel +import kotlinx.coroutines.flow.Flow + +/** Encapsulates business logic for interacting with notification settings. */ +class NotificationSettingsInteractor( + private val repository: NotificationSettingsRepository, +) { + /** The current state of the notification setting. */ + val settings: Flow<NotificationSettingsModel> = repository.settings + + /** Toggles the setting to show or hide notifications on the lock screen. */ + suspend fun toggleShowNotificationsOnLockScreenEnabled() { + val currentModel = repository.getSettings() + setSettings( + currentModel.copy( + isShowNotificationsOnLockScreenEnabled = + !currentModel.isShowNotificationsOnLockScreenEnabled, + ) + ) + } + + suspend fun setSettings(model: NotificationSettingsModel) { + repository.setSettings(model) + } + + suspend fun getSettings(): NotificationSettingsModel { + return repository.getSettings() + } +} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt new file mode 100644 index 000000000000..7e35360e30ff --- /dev/null +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.shared.notifications.shared.model + +/** Models notification settings. */ +data class NotificationSettingsModel( + /** Whether notifications are shown on the lock screen. */ + val isShowNotificationsOnLockScreenEnabled: Boolean = false, +) diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt new file mode 100644 index 000000000000..7ef16a8a4b23 --- /dev/null +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt @@ -0,0 +1,102 @@ +/* + * 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.shared.settings.data.repository + +import android.content.ContentResolver +import android.database.ContentObserver +import android.provider.Settings +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext + +/** Defines interface for classes that can provide access to data from [Settings.Secure]. */ +interface SecureSettingsRepository { + + /** Returns a [Flow] tracking the value of a setting as an [Int]. */ + fun intSetting( + name: String, + defaultValue: Int = 0, + ): Flow<Int> + + /** Updates the value of the setting with the given name. */ + suspend fun set( + name: String, + value: Int, + ) + + suspend fun get( + name: String, + defaultValue: Int = 0, + ): Int +} + +class SecureSettingsRepositoryImpl( + private val contentResolver: ContentResolver, + private val backgroundDispatcher: CoroutineDispatcher, +) : SecureSettingsRepository { + + override fun intSetting( + name: String, + defaultValue: Int, + ): Flow<Int> { + return callbackFlow { + val observer = + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + trySend(Unit) + } + } + + contentResolver.registerContentObserver( + Settings.Secure.getUriFor(name), + /* notifyForDescendants= */ false, + observer, + ) + send(Unit) + + awaitClose { contentResolver.unregisterContentObserver(observer) } + } + .map { Settings.Secure.getInt(contentResolver, name, defaultValue) } + // The above work is done on the background thread (which is important for accessing + // settings through the content resolver). + .flowOn(backgroundDispatcher) + } + + override suspend fun set(name: String, value: Int) { + withContext(backgroundDispatcher) { + Settings.Secure.putInt( + contentResolver, + name, + value, + ) + } + } + + override suspend fun get(name: String, defaultValue: Int): Int { + return withContext(backgroundDispatcher) { + Settings.Secure.getInt( + contentResolver, + name, + defaultValue, + ) + } + } +} diff --git a/packages/SystemUI/customization/tests/utils/Android.bp b/packages/SystemUI/customization/tests/utils/Android.bp new file mode 100644 index 000000000000..6db141030cce --- /dev/null +++ b/packages/SystemUI/customization/tests/utils/Android.bp @@ -0,0 +1,33 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + +java_library { + name: "SystemUICustomizationTestUtils", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + static_libs: [ + "SystemUICustomizationLib", + ], +} diff --git a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt new file mode 100644 index 000000000000..1c86a0745751 --- /dev/null +++ b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt @@ -0,0 +1,38 @@ +/* + * 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.shared.settings.data.repository + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map + +class FakeSecureSettingsRepository : SecureSettingsRepository { + + private val settings = MutableStateFlow<Map<String, String>>(mutableMapOf()) + + override fun intSetting(name: String, defaultValue: Int): Flow<Int> { + return settings.map { it.getOrDefault(name, defaultValue.toString()) }.map { it.toInt() } + } + + override suspend fun set(name: String, value: Int) { + settings.value = settings.value.toMutableMap().apply { this[name] = value.toString() } + } + + override suspend fun get(name: String, defaultValue: Int): Int { + return settings.value[name]?.toInt() ?: defaultValue + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt new file mode 100644 index 000000000000..3f05fef3c141 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import android.os.UserHandle +import android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionRepositoryImplTest : SysuiTestCase() { + + private val testDispatcher = StandardTestDispatcher() + private val scope = TestScope(testDispatcher) + private val settings: FakeSettings = FakeSettings() + + private lateinit var underTest: ColorInversionRepository + + @Before + fun setUp() { + underTest = + ColorInversionRepositoryImpl( + testDispatcher, + settings, + ) + } + + @Test + fun isEnabled_initiallyGetsSettingsValue() = + scope.runTest { + settings.putIntForUser(SETTING_NAME, 1, TEST_USER_1.identifier) + + underTest = + ColorInversionRepositoryImpl( + testDispatcher, + settings, + ) + + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + runCurrent() + + val actualValue: Boolean = underTest.isEnabled(TEST_USER_1).first() + assertThat(actualValue).isTrue() + } + + @Test + fun isEnabled_settingUpdated_valueUpdated() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + + settings.putIntForUser(SETTING_NAME, ENABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + } + + @Test + fun isEnabled_settingForUserOneOnly_valueUpdatedForUserOneOnly() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_1.identifier) + underTest.isEnabled(TEST_USER_2).launchIn(backgroundScope) + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_2.identifier) + + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + + settings.putIntForUser(SETTING_NAME, ENABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + } + + @Test + fun setEnabled() = + scope.runTest { + val success = underTest.setIsEnabled(true, TEST_USER_1) + runCurrent() + assertThat(success).isTrue() + + val actualValue = settings.getIntForUser(SETTING_NAME, TEST_USER_1.identifier) + assertThat(actualValue).isEqualTo(ENABLED) + } + + @Test + fun setDisabled() = + scope.runTest { + val success = underTest.setIsEnabled(false, TEST_USER_1) + runCurrent() + assertThat(success).isTrue() + + val actualValue = settings.getIntForUser(SETTING_NAME, TEST_USER_1.identifier) + assertThat(actualValue).isEqualTo(DISABLED) + } + + companion object { + private const val SETTING_NAME = ACCESSIBILITY_DISPLAY_INVERSION_ENABLED + private const val DISABLED = 0 + private const val ENABLED = 1 + private val TEST_USER_1 = UserHandle.of(1)!! + private val TEST_USER_2 = UserHandle.of(2)!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt new file mode 100644 index 000000000000..f3c3579966fd --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion.domain + +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tileimpl.SubtitleArrayMapping +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.android.systemui.qs.tiles.impl.inversion.qsColorInversionTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig + private val subtitleArrayId = + SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec) + private val subtitleArray = context.resources.getStringArray(subtitleArrayId) + // Using lazy (versus =) to make sure we override the right context -- see b/311612168 + private val mapper by lazy { + ColorInversionTileMapper( + context.orCreateTestableResources + .apply { + addOverride(R.drawable.qs_invert_colors_icon_off, TestStubDrawable()) + addOverride(R.drawable.qs_invert_colors_icon_on, TestStubDrawable()) + } + .resources, + context.theme + ) + } + + @Test + fun disabledModel() { + val inputModel = ColorInversionTileModel(false) + + val outputState = mapper.map(colorInversionTileConfig, inputModel) + + val expectedState = + createColorInversionTileState( + QSTileState.ActivationState.INACTIVE, + subtitleArray[1], + R.drawable.qs_invert_colors_icon_off + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun enabledModel() { + val inputModel = ColorInversionTileModel(true) + + val outputState = mapper.map(colorInversionTileConfig, inputModel) + + val expectedState = + createColorInversionTileState( + QSTileState.ActivationState.ACTIVE, + subtitleArray[2], + R.drawable.qs_invert_colors_icon_on + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + private fun createColorInversionTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String, + iconRes: Int, + ): QSTileState { + val label = context.getString(R.string.quick_settings_inversion_label) + return QSTileState( + { Icon.Loaded(context.getDrawable(iconRes)!!, null) }, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + label, + null, + QSTileState.SideViewIcon.None, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt new file mode 100644 index 000000000000..24c7bfb2d324 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository +import com.android.systemui.coroutines.collectValues +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toCollection +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionTileDataInteractorTest : SysuiTestCase() { + + private val colorInversionRepository = FakeColorInversionRepository() + private val underTest: ColorInversionTileDataInteractor = + ColorInversionTileDataInteractor(colorInversionRepository) + + @Test + fun alwaysAvailable() = runTest { + val availability = underTest.availability(TEST_USER).toCollection(mutableListOf()) + + assertThat(availability).hasSize(1) + assertThat(availability.last()).isTrue() + } + + @Test + fun dataMatchesTheRepository() = runTest { + val dataList: List<ColorInversionTileModel> by + collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))) + runCurrent() + + colorInversionRepository.setIsEnabled(true, TEST_USER) + runCurrent() + + colorInversionRepository.setIsEnabled(false, TEST_USER) + runCurrent() + + assertThat(dataList).hasSize(3) + assertThat(dataList.map { it.isEnabled }).isEqualTo(listOf(false, true, false)) + } + + private companion object { + val TEST_USER = UserHandle.of(1)!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt new file mode 100644 index 000000000000..99bae18d43d4 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.os.UserHandle +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionUserActionInteractorTest : SysuiTestCase() { + + private val testUser = UserHandle.CURRENT + private val repository = FakeColorInversionRepository() + private val inputHandler = FakeQSTileIntentUserInputHandler() + + private val underTest = + ColorInversionUserActionInteractor( + repository, + inputHandler, + ) + + @Test + fun handleClickWhenEnabled() = runTest { + val wasEnabled = true + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorInversionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleClickWhenDisabled() = runTest { + val wasEnabled = false + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorInversionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleLongClickWhenDisabled() = runTest { + val enabled = false + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorInversionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_INVERSION_SETTINGS) + } + } + + @Test + fun handleLongClickWhenEnabled() = runTest { + val enabled = true + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorInversionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_INVERSION_SETTINGS) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt index 886c61ae29dd..bf33010a055b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt @@ -27,7 +27,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserTracker -import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter +import com.android.systemui.smartspace.filters.LockscreenTargetFilter import com.android.systemui.util.concurrency.Execution import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -52,55 +52,52 @@ import org.mockito.MockitoAnnotations @SmallTest @TestableLooper.RunWithLooper @RunWith(AndroidJUnit4::class) -class LockscreenAndDreamTargetFilterTest : SysuiTestCase() { - @Mock - private lateinit var secureSettings: SecureSettings +class LockscreenTargetFilterTest : SysuiTestCase() { + @Mock private lateinit var secureSettings: SecureSettings - @Mock - private lateinit var userTracker: UserTracker + @Mock private lateinit var userTracker: UserTracker - @Mock - private lateinit var execution: Execution + @Mock private lateinit var execution: Execution - @Mock - private lateinit var handler: Handler + @Mock private lateinit var handler: Handler - @Mock - private lateinit var contentResolver: ContentResolver + @Mock private lateinit var contentResolver: ContentResolver - @Mock - private lateinit var uiExecution: Executor + @Mock private lateinit var uiExecution: Executor - @Mock - private lateinit var userHandle: UserHandle + @Mock private lateinit var userHandle: UserHandle - @Mock - private lateinit var listener: SmartspaceTargetFilter.Listener + @Mock private lateinit var listener: SmartspaceTargetFilter.Listener - @Mock - private lateinit var lockScreenAllowPrivateNotificationsUri: Uri + @Mock private lateinit var lockScreenAllowPrivateNotificationsUri: Uri @Before fun setup() { MockitoAnnotations.initMocks(this) `when`(userTracker.userHandle).thenReturn(userHandle) - `when`(secureSettings - .getUriFor(eq(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS))) - .thenReturn(lockScreenAllowPrivateNotificationsUri) + `when`( + secureSettings.getUriFor( + eq(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS) + ) + ) + .thenReturn(lockScreenAllowPrivateNotificationsUri) } - /** - * Ensures {@link Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS} is - * tracked. - */ + /** Ensures {@link Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS} is tracked. */ @Test fun testLockscreenAllowPrivateNotifications() { var setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS - `when`(secureSettings - .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt())) - .thenReturn(0) - var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler, - contentResolver, uiExecution) + `when`(secureSettings.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt())) + .thenReturn(0) + var filter = + LockscreenTargetFilter( + secureSettings, + userTracker, + execution, + handler, + contentResolver, + uiExecution + ) filter.addListener(listener) var smartspaceTarget = mock(SmartspaceTarget::class.java) @@ -110,12 +107,16 @@ class LockscreenAndDreamTargetFilterTest : SysuiTestCase() { var settingCaptor = ArgumentCaptor.forClass(ContentObserver::class.java) - verify(contentResolver).registerContentObserver(eq(lockScreenAllowPrivateNotificationsUri), - anyBoolean(), settingCaptor.capture(), anyInt()) + verify(contentResolver) + .registerContentObserver( + eq(lockScreenAllowPrivateNotificationsUri), + anyBoolean(), + settingCaptor.capture(), + anyInt() + ) - `when`(secureSettings - .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt())) - .thenReturn(1) + `when`(secureSettings.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt())) + .thenReturn(1) clearInvocations(listener) settingCaptor.value.onChange(false, mock(Uri::class.java)) @@ -123,23 +124,28 @@ class LockscreenAndDreamTargetFilterTest : SysuiTestCase() { assertThat(filter.filterSmartspaceTarget(smartspaceTarget)).isTrue() } - /** - * Ensures user switches are tracked. - */ + /** Ensures user switches are tracked. */ @Test fun testUserSwitchCallback() { var setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS - `when`(secureSettings - .getIntForUser(eq(setting) ?: setting, anyInt(), anyInt())) - .thenReturn(0) - var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler, - contentResolver, uiExecution) + `when`(secureSettings.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt())) + .thenReturn(0) + var filter = + LockscreenTargetFilter( + secureSettings, + userTracker, + execution, + handler, + contentResolver, + uiExecution + ) filter.addListener(listener) - var userTrackerCallback = withArgCaptor<UserTracker.Callback> { - verify(userTracker).addCallback(capture(), any()) - } + var userTrackerCallback = + withArgCaptor<UserTracker.Callback> { + verify(userTracker).addCallback(capture(), any()) + } clearInvocations(listener) userTrackerCallback.onUserChanged(0, context) diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags index 21b019eacdcc..f9546c46e915 100644 --- a/packages/SystemUI/proguard_common.flags +++ b/packages/SystemUI/proguard_common.flags @@ -38,6 +38,11 @@ -keepnames class com.android.internal.protolog.** { *; } -keepnames class android.hardware.common.** { *; } +# TODO(b/316553881): Statically linking flags into SystemUI and bootclasspath might not be a +# good idea in the first place +-keepnames class com.android.window.flags.Flags { public *; } + + # Allows proguard to make private and protected methods and fields public as # part of optimization. This lets proguard inline trivial getter/setter methods. -allowaccessmodification diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml index 4336ccc70c33..4535f67fa7f9 100644 --- a/packages/SystemUI/res/layout/bluetooth_device_item.xml +++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml @@ -41,7 +41,7 @@ android:textDirection="locale" android:textAlignment="gravity" android:paddingStart="20dp" - android:paddingTop="10dp" + android:paddingTop="15dp" android:maxLines="1" android:ellipsize="end" app:layout_constraintTop_toTopOf="parent" @@ -56,7 +56,7 @@ android:id="@+id/bluetooth_device_summary" style="@style/BluetoothTileDialog.DeviceSummary" android:paddingStart="20dp" - android:paddingBottom="10dp" + android:paddingBottom="15dp" android:maxLines="1" android:ellipsize="end" app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name" diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml index 50241cdca8b5..0534c6ee466f 100644 --- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml +++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml @@ -51,13 +51,14 @@ android:textAppearance="@style/TextAppearance.Dialog.Body.Message" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toTopOf="@+id/scroll_view" app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_title" /> <androidx.core.widget.NestedScrollView android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="21dp" + android:fillViewport="true" app:layout_constrainedHeight="true" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -84,13 +85,13 @@ android:textSize="16sp" app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toTopOf="@+id/device_list" app:layout_constraintTop_toTopOf="parent" /> <Switch android:id="@+id/bluetooth_toggle" android:layout_width="wrap_content" android:layout_height="48dp" + android:paddingTop="10dp" android:gravity="start|center_vertical" android:paddingEnd="40dp" android:contentDescription="@string/turn_on_bluetooth" @@ -100,14 +101,13 @@ android:track="@drawable/settingslib_track_selector" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title" - app:layout_constraintBottom_toTopOf="@+id/device_list" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/device_list" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp" + android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle" @@ -146,6 +146,7 @@ android:background="@drawable/bluetooth_tile_dialog_bg_off" android:layout_width="0dp" android:layout_height="64dp" + android:layout_marginBottom="9dp" android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -177,7 +178,6 @@ android:maxLines="1" android:text="@string/inline_done_button" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/pair_new_device_button" app:layout_constraintBottom_toBottomOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.core.widget.NestedScrollView> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index ca0fb85beb6e..87c31c8b8aae 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -26,6 +26,12 @@ android:layout_height="match_parent" android:fitsSystemWindows="true"> + <!-- Placeholder for the communal UI that will be replaced if the feature is enabled. --> + <ViewStub + android:id="@+id/communal_ui_stub" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + <com.android.systemui.scrim.ScrimView android:id="@+id/scrim_behind" android:layout_width="match_parent" @@ -64,12 +70,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - <!-- Placeholder for the communal UI that will be replaced if the feature is enabled. --> - <ViewStub - android:id="@+id/communal_ui_stub" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <include layout="@layout/brightness_mirror_container" /> <com.android.systemui.scrim.ScrimView diff --git a/packages/SystemUI/res/layout/widget_picker.xml b/packages/SystemUI/res/layout/widget_picker.xml index 827bd5d2e0b1..21dc224a6f14 100644 --- a/packages/SystemUI/res/layout/widget_picker.xml +++ b/packages/SystemUI/res/layout/widget_picker.xml @@ -14,13 +14,17 @@ ~ limitations under the License. --> -<LinearLayout +<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/widgets_container" android:layout_width="match_parent" - android:layout_height="match_parent" - android:padding="64dp" - android:gravity="center_vertical" - android:orientation="horizontal"> + android:layout_height="match_parent"> -</LinearLayout> + <LinearLayout + android:id="@+id/widgets_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="horizontal"> + </LinearLayout> + +</HorizontalScrollView> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 33a0a06e940c..deed6c8c7f25 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -543,12 +543,16 @@ <!-- ID for the camera of outer display that needs extra protection --> <string translatable="false" name="config_protectedCameraId"></string> + <!-- Physical ID for the camera of outer display that needs extra protection --> + <string translatable="false" name="config_protectedPhysicalCameraId"></string> <!-- Similar to config_frontBuiltInDisplayCutoutProtection but for inner display. --> <string translatable="false" name="config_innerBuiltInDisplayCutoutProtection"></string> <!-- ID for the camera of inner display that needs extra protection --> <string translatable="false" name="config_protectedInnerCameraId"></string> + <!-- Physical ID for the camera of inner display that needs extra protection --> + <string translatable="false" name="config_protectedInnerPhysicalCameraId"></string> <!-- Comma-separated list of packages to exclude from camera protection e.g. "com.android.systemui,com.android.xyz" --> diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt index d33d279023da..5f5cca86cce6 100644 --- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt +++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt @@ -38,26 +38,75 @@ class CameraAvailabilityListener( excludedPackages: String, private val executor: Executor ) { + private var activeProtectionInfo: CameraProtectionInfo? = null + private var openCamera: OpenCameraInfo? = null + private val unavailablePhysicalCameras = mutableSetOf<String>() private val excludedPackageIds: Set<String> private val listeners = mutableListOf<CameraTransitionCallback>() private val availabilityCallback: CameraManager.AvailabilityCallback = - object : CameraManager.AvailabilityCallback() { - override fun onCameraClosed(cameraId: String) { - cameraProtectionInfoList.forEach { - if (cameraId == it.cameraId) { - notifyCameraInactive() - } + object : CameraManager.AvailabilityCallback() { + override fun onCameraClosed(logicalCameraId: String) { + openCamera = null + if (activeProtectionInfo?.logicalCameraId == logicalCameraId) { + notifyCameraInactive() + } + activeProtectionInfo = null + } + + override fun onCameraOpened(logicalCameraId: String, packageId: String) { + openCamera = OpenCameraInfo(logicalCameraId, packageId) + if (isExcluded(packageId)) { + return + } + val protectionInfo = + cameraProtectionInfoList.firstOrNull { + logicalCameraId == it.logicalCameraId && + it.physicalCameraId !in unavailablePhysicalCameras } + if (protectionInfo != null) { + activeProtectionInfo = protectionInfo + notifyCameraActive(protectionInfo) } + } - override fun onCameraOpened(cameraId: String, packageId: String) { - cameraProtectionInfoList.forEach { - if (cameraId == it.cameraId && !isExcluded(packageId)) { - notifyCameraActive(it) - } + override fun onPhysicalCameraAvailable( + logicalCameraId: String, + physicalCameraId: String + ) { + unavailablePhysicalCameras -= physicalCameraId + val openCamera = this@CameraAvailabilityListener.openCamera ?: return + if (openCamera.logicalCameraId != logicalCameraId) { + return + } + if (isExcluded(openCamera.packageId)) { + return + } + val newActiveInfo = + cameraProtectionInfoList.find { + it.logicalCameraId == logicalCameraId && + it.physicalCameraId == physicalCameraId } + if (newActiveInfo != null) { + activeProtectionInfo = newActiveInfo + notifyCameraActive(newActiveInfo) } - } + } + + override fun onPhysicalCameraUnavailable( + logicalCameraId: String, + physicalCameraId: String + ) { + unavailablePhysicalCameras += physicalCameraId + val activeInfo = activeProtectionInfo ?: return + if ( + activeInfo.logicalCameraId == logicalCameraId && + activeInfo.physicalCameraId == physicalCameraId + ) { + activeProtectionInfo = null + notifyCameraInactive() + } + } + } init { excludedPackageIds = excludedPackages.split(",").toSet() @@ -106,24 +155,21 @@ class CameraAvailabilityListener( listeners.forEach { it.onHideCameraProtection() } } - /** - * Callbacks to tell a listener that a relevant camera turned on and off. - */ + /** Callbacks to tell a listener that a relevant camera turned on and off. */ interface CameraTransitionCallback { fun onApplyCameraProtection(protectionPath: Path, bounds: Rect) + fun onHideCameraProtection() } companion object Factory { fun build(context: Context, executor: Executor): CameraAvailabilityListener { - val manager = context - .getSystemService(Context.CAMERA_SERVICE) as CameraManager + val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager val res = context.resources val cameraProtectionInfoList = loadCameraProtectionInfoList(res) val excluded = res.getString(R.string.config_cameraProtectionExcludedPackages) - return CameraAvailabilityListener( - manager, cameraProtectionInfoList, excluded, executor) + return CameraAvailabilityListener(manager, cameraProtectionInfoList, excluded, executor) } private fun pathFromString(pathString: String): Path { @@ -140,19 +186,23 @@ class CameraAvailabilityListener( private fun loadCameraProtectionInfoList(res: Resources): List<CameraProtectionInfo> { val list = mutableListOf<CameraProtectionInfo>() - val front = loadCameraProtectionInfo( + val front = + loadCameraProtectionInfo( res, R.string.config_protectedCameraId, + R.string.config_protectedPhysicalCameraId, R.string.config_frontBuiltInDisplayCutoutProtection - ) + ) if (front != null) { list.add(front) } - val inner = loadCameraProtectionInfo( + val inner = + loadCameraProtectionInfo( res, R.string.config_protectedInnerCameraId, + R.string.config_protectedInnerPhysicalCameraId, R.string.config_innerBuiltInDisplayCutoutProtection - ) + ) if (inner != null) { list.add(inner) } @@ -160,30 +210,44 @@ class CameraAvailabilityListener( } private fun loadCameraProtectionInfo( - res: Resources, - cameraIdRes: Int, - pathRes: Int + res: Resources, + cameraIdRes: Int, + physicalCameraIdRes: Int, + pathRes: Int ): CameraProtectionInfo? { - val cameraId = res.getString(cameraIdRes) - if (cameraId == null || cameraId.isEmpty()) { + val logicalCameraId = res.getString(cameraIdRes) + if (logicalCameraId.isNullOrEmpty()) { return null } + val physicalCameraId = res.getString(physicalCameraIdRes) val protectionPath = pathFromString(res.getString(pathRes)) val computed = RectF() protectionPath.computeBounds(computed) - val protectionBounds = Rect( + val protectionBounds = + Rect( computed.left.roundToInt(), computed.top.roundToInt(), computed.right.roundToInt(), computed.bottom.roundToInt() + ) + return CameraProtectionInfo( + logicalCameraId, + physicalCameraId, + protectionPath, + protectionBounds ) - return CameraProtectionInfo(cameraId, protectionPath, protectionBounds) } } - data class CameraProtectionInfo ( - val cameraId: String, - val cutoutProtectionPath: Path, - val cutoutBounds: Rect + data class CameraProtectionInfo( + val logicalCameraId: String, + val physicalCameraId: String?, + val cutoutProtectionPath: Path, + val cutoutBounds: Rect, + ) + + private data class OpenCameraInfo( + val logicalCameraId: String, + val packageId: String, ) } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 70750a1dafb5..0be206d1f75a 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -73,7 +73,8 @@ public final class Prefs { Key.ACCESSIBILITY_FLOATING_MENU_POSITION, Key.HAS_CLICKED_NUDGE_TO_SETUP_DREAM, Key.HAS_DISMISSED_NUDGE_TO_SETUP_DREAM, - Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED + Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED, + Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT, }) // TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them public @interface Key { @@ -119,6 +120,7 @@ public final class Prefs { String HAS_CLICKED_NUDGE_TO_SETUP_DREAM = "HasClickedNudgeToSetupDream"; String HAS_DISMISSED_NUDGE_TO_SETUP_DREAM = "HasDismissedNudgeToSetupDream"; String HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED = "HasAccessibilityFloatingMenuTucked"; + String BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT = "BluetoothTileDialogContentHeight"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 6faee8cd1c4f..008de4387c4a 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -179,6 +179,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable { @VisibleForTesting protected DisplayInfo mDisplayInfo = new DisplayInfo(); private DisplayCutout mDisplayCutout; + private boolean mPendingManualConfigUpdate; @VisibleForTesting protected void showCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) { @@ -571,6 +572,11 @@ public class ScreenDecorations implements CoreStartable, Dumpable { setupDecorations(); return; } + + if (mPendingManualConfigUpdate) { + mPendingManualConfigUpdate = false; + onConfigurationChanged(mContext.getResources().getConfiguration()); + } } } }; @@ -1064,6 +1070,15 @@ public class ScreenDecorations implements CoreStartable, Dumpable { mExecutor.execute(() -> { Trace.beginSection("ScreenDecorations#onConfigurationChanged"); + mContext.getDisplay().getDisplayInfo(mDisplayInfo); + if (displaySizeChanged(mDisplaySize, mDisplayInfo)) { + // We expect the display change event to happen first, but in this case, we received + // onConfigurationChanged first. + // Return so that we handle the display change event first, and then manually + // trigger the config update. + mPendingManualConfigUpdate = true; + return; + } int oldRotation = mRotation; mPendingConfigChange = false; updateConfiguration(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt index 24aa11e10f30..8c2d221e3f97 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt @@ -18,6 +18,8 @@ package com.android.systemui.accessibility import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository import com.android.systemui.accessibility.data.repository.ColorCorrectionRepositoryImpl +import com.android.systemui.accessibility.data.repository.ColorInversionRepository +import com.android.systemui.accessibility.data.repository.ColorInversionRepositoryImpl import com.android.systemui.accessibility.qs.QSAccessibilityModule import dagger.Binds import dagger.Module @@ -25,7 +27,8 @@ import dagger.Module @Module(includes = [QSAccessibilityModule::class]) interface AccessibilityModule { @Binds - abstract fun colorCorrectionRepository( - impl: ColorCorrectionRepositoryImpl - ): ColorCorrectionRepository + fun colorCorrectionRepository(impl: ColorCorrectionRepositoryImpl): ColorCorrectionRepository + + @Binds + fun colorInversionRepository(impl: ColorInversionRepositoryImpl): ColorInversionRepository } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt new file mode 100644 index 000000000000..bbf10c509e50 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import android.os.UserHandle +import android.provider.Settings.Secure +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext + +/** Provides data related to color inversion. */ +interface ColorInversionRepository { + /** Observable for whether color inversion is enabled */ + fun isEnabled(userHandle: UserHandle): Flow<Boolean> + + /** Sets color inversion enabled state. */ + suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean +} + +@SysUISingleton +class ColorInversionRepositoryImpl +@Inject +constructor( + @Background private val bgCoroutineContext: CoroutineContext, + private val secureSettings: SecureSettings, +) : ColorInversionRepository { + + override fun isEnabled(userHandle: UserHandle): Flow<Boolean> = + secureSettings + .observerFlow(userHandle.identifier, SETTING_NAME) + .onStart { emit(Unit) } + .map { secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED } + .distinctUntilChanged() + .flowOn(bgCoroutineContext) + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean = + withContext(bgCoroutineContext) { + secureSettings.putIntForUser( + SETTING_NAME, + if (isEnabled) ENABLED else DISABLED, + userHandle.identifier + ) + } + + companion object { + private const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED + private const val DISABLED = 0 + private const val ENABLED = 1 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt index df7fdb8e6058..135ab35a4681 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt @@ -31,6 +31,10 @@ import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrection import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper +import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel @@ -87,8 +91,8 @@ interface QSAccessibilityModule { fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*> companion object { - const val COLOR_CORRECTION_TILE_SPEC = "color_correction" + const val COLOR_INVERSION_TILE_SPEC = "inversion" @Provides @IntoMap @@ -120,5 +124,36 @@ interface QSAccessibilityModule { stateInteractor, mapper, ) + + @Provides + @IntoMap + @StringKey(COLOR_INVERSION_TILE_SPEC) + fun provideColorInversionTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(COLOR_INVERSION_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.qs_invert_colors_icon_off, + labelRes = R.string.quick_settings_inversion_label, + ), + instanceId = uiEventLogger.getNewInstanceId(), + ) + + /** Inject ColorInversionTile into tileViewModelMap in QSModule */ + @Provides + @IntoMap + @StringKey(COLOR_INVERSION_TILE_SPEC) + fun provideColorInversionTileViewModel( + factory: QSTileViewModelFactory.Static<ColorInversionTileModel>, + mapper: ColorInversionTileMapper, + stateInteractor: ColorInversionTileDataInteractor, + userActionInteractor: ColorInversionUserActionInteractor + ): QSTileViewModel = + factory.create( + TileSpec.create(COLOR_INVERSION_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt index c5610c877f57..75f9d809cc69 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt @@ -30,9 +30,9 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB import com.android.systemui.smartspace.SmartspacePrecondition import com.android.systemui.smartspace.SmartspaceTargetFilter -import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION -import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_PRECONDITION +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_TARGET_FILTER import com.android.systemui.util.concurrency.Execution import java.util.Optional import java.util.concurrent.Executor @@ -48,8 +48,8 @@ constructor( private val smartspaceManager: SmartspaceManager?, private val execution: Execution, @Main private val uiExecutor: Executor, - @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition, - @Named(DREAM_SMARTSPACE_TARGET_FILTER) + @Named(LOCKSCREEN_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition, + @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER) private val optionalTargetFilter: Optional<SmartspaceTargetFilter>, @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>, ) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 573a748b4290..887b18cfe4c9 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -19,7 +19,9 @@ package com.android.systemui.communal.widgets import android.appwidget.AppWidgetProviderInfo import android.content.Intent import android.os.Bundle +import android.os.RemoteException import android.util.Log +import android.view.IWindowManager import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult @@ -34,6 +36,7 @@ class EditWidgetsActivity constructor( private val communalViewModel: CommunalEditModeViewModel, private val communalInteractor: CommunalInteractor, + private var windowManagerService: IWindowManager? = null, ) : ComponentActivity() { companion object { /** @@ -77,8 +80,12 @@ constructor( ) }, onEditDone = { - // TODO(b/315154364): in a separate change, lock the device and transition to GH - finish() + try { + checkNotNull(windowManagerService).lockNow(/* options */ null) + finish() + } catch (e: RemoteException) { + Log.e(TAG, "Couldn't lock the device as WindowManager is dead.") + } } ) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt index a2765486bf2d..a26afc86aa2e 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt @@ -19,13 +19,15 @@ package com.android.systemui.communal.widgets import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProviderInfo import android.content.Intent +import android.graphics.Color import android.os.Bundle -import android.util.DisplayMetrics import android.util.Log import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout import androidx.activity.ComponentActivity +import androidx.core.view.setMargins +import androidx.core.view.setPadding import com.android.systemui.res.R import javax.inject.Inject @@ -43,7 +45,6 @@ constructor( super.onCreate(savedInstanceState) setContentView(R.layout.widget_picker) - loadWidgets() } @@ -54,24 +55,38 @@ constructor( appWidgetManager .getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) ?.stream() - ?.limit(5) ?.forEach { widgetInfo -> val activity = this@WidgetPickerActivity - val widgetPreview = - widgetInfo.loadPreviewImage(activity, DisplayMetrics.DENSITY_HIGH) - val widgetView = ImageView(activity) - val lp = LinearLayout.LayoutParams(WIDGET_PREVIEW_SIZE, WIDGET_PREVIEW_SIZE) - widgetView.setLayoutParams(lp) - widgetView.setImageDrawable(widgetPreview) - widgetView.setOnClickListener({ - setResult( - RESULT_OK, - Intent().putExtra(EditWidgetsActivity.ADD_WIDGET_INFO, widgetInfo) - ) - finish() - }) - - addView(widgetView) + (widgetInfo.loadPreviewImage(activity, 0) + ?: widgetInfo.loadIcon(activity, 0)) + ?.let { + addView( + ImageView(activity).also { v -> + v.setImageDrawable(it) + v.setBackgroundColor(WIDGET_PREVIEW_BACKGROUND_COLOR) + v.setPadding(WIDGET_PREVIEW_PADDING) + v.layoutParams = + LinearLayout.LayoutParams( + WIDGET_PREVIEW_SIZE, + WIDGET_PREVIEW_SIZE + ) + .also { lp -> + lp.setMargins(WIDGET_PREVIEW_MARGINS) + } + v.setOnClickListener { + setResult( + RESULT_OK, + Intent() + .putExtra( + EditWidgetsActivity.ADD_WIDGET_INFO, + widgetInfo + ) + ) + finish() + } + } + ) + } } } catch (e: RuntimeException) { Log.e(TAG, "Exception fetching widget providers", e) @@ -80,7 +95,10 @@ constructor( } companion object { - private const val WIDGET_PREVIEW_SIZE = 400 + private const val WIDGET_PREVIEW_SIZE = 600 + private const val WIDGET_PREVIEW_MARGINS = 32 + private const val WIDGET_PREVIEW_PADDING = 32 + private val WIDGET_PREVIEW_BACKGROUND_COLOR = Color.rgb(216, 225, 220) private const val TAG = "WidgetPickerActivity" } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt index ca4ef0d0bd8f..4a8c040e33c1 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt @@ -34,9 +34,9 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM import com.android.systemui.smartspace.SmartspacePrecondition import com.android.systemui.smartspace.SmartspaceTargetFilter import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_DATA_PLUGIN -import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION -import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_PRECONDITION +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.LOCKSCREEN_SMARTSPACE_TARGET_FILTER import com.android.systemui.smartspace.dagger.SmartspaceViewComponent import com.android.systemui.util.concurrency.Execution import java.util.Optional @@ -54,8 +54,8 @@ class DreamSmartspaceController @Inject constructor( private val execution: Execution, @Main private val uiExecutor: Executor, private val smartspaceViewComponentFactory: SmartspaceViewComponent.Factory, - @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition, - @Named(DREAM_SMARTSPACE_TARGET_FILTER) + @Named(LOCKSCREEN_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition, + @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER) private val optionalTargetFilter: Optional<SmartspaceTargetFilter>, @Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>, @Named(DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN) diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index b1d4587c20d8..b43f54de4b2a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -564,6 +564,13 @@ object Flags { val ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS = unreleasedFlag("enable_unfold_status_bar_animations") + // TODO(b/316157842): Tracking Bug + // Adds extra delay to notifications measure + @Keep + @JvmField + val ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE = + unreleasedFlag("enable_notifications_simulate_slow_measure") + // TODO(b259590361): Tracking bug val EXPERIMENTAL_FLAG = unreleasedFlag("exp_flag_release") diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 45433e62f214..3de9e68909cf 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -1054,6 +1054,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene return; } Trace.instantForTrack(Trace.TRACE_TAG_APP, "bugreport", "BugReportAction#onPress"); + Log.d(TAG, "BugReportAction#onPress"); // Add a little delay before executing, to give the // dialog a chance to go away before it takes a // screenshot. @@ -1069,6 +1070,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene Log.w(TAG, "Bugreport handler could not be launched"); Trace.instantForTrack(Trace.TRACE_TAG_APP, "bugreport", "BugReportAction#requestingInteractiveBugReport"); + Log.d(TAG, "BugReportAction#requestingInteractiveBugReport"); mIActivityManager.requestInteractiveBugReport(); } } catch (RemoteException e) { @@ -1090,6 +1092,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS); Trace.instantForTrack(Trace.TRACE_TAG_APP, "bugreport", "BugReportAction#requestingFullBugReport"); + Log.d(TAG, "BugReportAction#requestingFullBugReport"); mIActivityManager.requestFullBugReport(); } catch (RemoteException e) { } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt index cae6147b03ee..f5bcab96a5a4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt @@ -80,6 +80,11 @@ enum class KeyguardState { return state != GONE } + /** Whether either of the bouncers are visible when we're FINISHED in the given state. */ + fun isBouncerState(state: KeyguardState): Boolean { + return state == PRIMARY_BOUNCER || state == ALTERNATE_BOUNCER + } + /** * Whether the device is awake ([PowerInteractor.isAwake]) when we're FINISHED in the given * keyguard state. @@ -107,4 +112,4 @@ enum class KeyguardState { return !deviceIsAwakeInState(state) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java index 789a1e401ae6..e08eb37b79ba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -66,7 +66,8 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class TileLifecycleManager extends BroadcastReceiver implements IQSTileService, ServiceConnection, IBinder.DeathRecipient { - public static final boolean DEBUG = false; + + private final boolean mDebug = Log.isLoggable(TAG, Log.DEBUG); private static final String TAG = "TileLifecycleManager"; @@ -101,7 +102,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements private Set<Integer> mQueuedMessages = new ArraySet<>(); @Nullable - private QSTileServiceWrapper mWrapper; + private volatile QSTileServiceWrapper mWrapper; private boolean mListening; private IBinder mClickBinder; @@ -132,7 +133,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements mPackageManagerAdapter = packageManagerAdapter; mBroadcastDispatcher = broadcastDispatcher; mActivityManager = activityManager; - if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser); + if (mDebug) Log.d(TAG, "Creating " + mIntent + " " + mUser); } /** Injectable factory for TileLifecycleManager. */ @@ -215,10 +216,11 @@ public class TileLifecycleManager extends BroadcastReceiver implements if (!checkComponentState()) { return; } - if (DEBUG) Log.d(TAG, "Binding service " + mIntent + " " + mUser); + if (mDebug) Log.d(TAG, "Binding service " + mIntent + " " + mUser); mBindTryCount++; try { - mIsBound.set(bindServices()); + // Only try a new binding if we are not currently bound. + mIsBound.compareAndSet(false, bindServices()); if (!mIsBound.get()) { mContext.unbindService(this); } @@ -227,19 +229,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements mIsBound.set(false); } } else { - if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent + " " + mUser); - // Give it another chance next time it needs to be bound, out of kindness. - mBindTryCount = 0; - freeWrapper(); - if (mIsBound.get()) { - try { - mContext.unbindService(this); - } catch (Exception e) { - Log.e(TAG, "Failed to unbind service " - + mIntent.getComponent().flattenToShortString(), e); - } - mIsBound.set(false); - } + unbindService(); } } @@ -264,9 +254,26 @@ public class TileLifecycleManager extends BroadcastReceiver implements mUser); } + @WorkerThread + private void unbindService() { + if (mDebug) Log.d(TAG, "Unbinding service " + mIntent + " " + mUser); + // Give it another chance next time it needs to be bound, out of kindness. + mBindTryCount = 0; + freeWrapper(); + if (mIsBound.get()) { + try { + mContext.unbindService(this); + } catch (Exception e) { + Log.e(TAG, "Failed to unbind service " + + mIntent.getComponent().flattenToShortString(), e); + } + mIsBound.set(false); + } + } + @Override public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) Log.d(TAG, "onServiceConnected " + name); + if (mDebug) Log.d(TAG, "onServiceConnected " + name); // Got a connection, set the binding count to 0. mBindTryCount = 0; final QSTileServiceWrapper wrapper = new QSTileServiceWrapper(Stub.asInterface(service)); @@ -284,11 +291,17 @@ public class TileLifecycleManager extends BroadcastReceiver implements } @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name); + public void onBindingDied(ComponentName name) { + if (mDebug) Log.d(TAG, "onBindingDied " + name); handleDeath(); } + @Override + public void onServiceDisconnected(ComponentName name) { + if (mDebug) Log.d(TAG, "onServiceDisconnected " + name); + freeWrapper(); + } + private void handlePendingMessages() { // This ordering is laid out manually to make sure we preserve the TileService // lifecycle. @@ -298,35 +311,36 @@ public class TileLifecycleManager extends BroadcastReceiver implements mQueuedMessages.clear(); } if (queue.contains(MSG_ON_ADDED)) { - if (DEBUG) Log.d(TAG, "Handling pending onAdded"); + if (mDebug) Log.d(TAG, "Handling pending onAdded " + getComponent()); onTileAdded(); } if (mListening) { - if (DEBUG) Log.d(TAG, "Handling pending onStartListening"); + if (mDebug) Log.d(TAG, "Handling pending onStartListening " + getComponent()); onStartListening(); } if (queue.contains(MSG_ON_CLICK)) { - if (DEBUG) Log.d(TAG, "Handling pending onClick"); + if (mDebug) Log.d(TAG, "Handling pending onClick " + getComponent()); if (!mListening) { - Log.w(TAG, "Managed to get click on non-listening state..."); + Log.w(TAG, "Managed to get click on non-listening state... " + getComponent()); // Skipping click since lost click privileges. } else { onClick(mClickBinder); } } if (queue.contains(MSG_ON_UNLOCK_COMPLETE)) { - if (DEBUG) Log.d(TAG, "Handling pending onUnlockComplete"); + if (mDebug) Log.d(TAG, "Handling pending onUnlockComplete " + getComponent()); if (!mListening) { - Log.w(TAG, "Managed to get unlock on non-listening state..."); + Log.w(TAG, + "Managed to get unlock on non-listening state... " + getComponent()); // Skipping unlock since lost click privileges. } else { onUnlockComplete(); } } if (queue.contains(MSG_ON_REMOVED)) { - if (DEBUG) Log.d(TAG, "Handling pending onRemoved"); + if (mDebug) Log.d(TAG, "Handling pending onRemoved " + getComponent()); if (mListening) { - Log.w(TAG, "Managed to get remove in listening state..."); + Log.w(TAG, "Managed to get remove in listening state... " + getComponent()); onStopListening(); } onTileRemoved(); @@ -340,28 +354,44 @@ public class TileLifecycleManager extends BroadcastReceiver implements } public void handleDestroy() { - if (DEBUG) Log.d(TAG, "handleDestroy"); + if (mDebug) Log.d(TAG, "handleDestroy"); if (mPackageReceiverRegistered.get() || mUserReceiverRegistered.get()) { stopPackageListening(); } mChangeListener = null; } + /** + * Handles a dead binder. + * + * It means that we need to clean up the binding (calling unbindService). After that, if we + * are supposed to be bound, we will try to bind after some amount of time. + */ private void handleDeath() { - if (mWrapper == null) return; - freeWrapper(); - // Clearly not bound anymore - mIsBound.set(false); - if (!mBound.get()) return; - if (DEBUG) Log.d(TAG, "handleDeath"); - if (checkComponentState()) { - if (isDeathRebindScheduled.compareAndSet(false, true)) { - mExecutor.executeDelayed(() -> { - setBindService(true); - isDeathRebindScheduled.set(false); - }, getRebindDelay()); + mExecutor.execute(() -> { + if (!mIsBound.get()) { + // If we are already not bound, don't do anything else. + return; } - } + // Clearly we shouldn't be bound anymore + if (mDebug) Log.d(TAG, "handleDeath " + getComponent()); + // Binder died, make sure that we unbind. However, we don't want to call setBindService + // as we still may want to rebind. + unbindService(); + // If mBound is true (meaning that we should be bound), then reschedule binding for + // later. + if (mBound.get() && checkComponentState()) { + if (isDeathRebindScheduled.compareAndSet(false, true)) { + mExecutor.executeDelayed(() -> { + // Only rebind if we are supposed to, but remove the scheduling anyway. + if (mBound.get()) { + setBindService(true); + } + isDeathRebindScheduled.set(false); + }, getRebindDelay()); + } + } + }); } /** @@ -379,7 +409,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements } else { delay = mBindRetryDelay; } - Log.i(TAG, "Rebinding with a delay=" + delay); + if (mDebug) Log.i(TAG, "Rebinding with a delay=" + delay + " - " + getComponent()); return delay; } @@ -392,7 +422,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements } private void startPackageListening() { - if (DEBUG) Log.d(TAG, "startPackageListening"); + if (mDebug) Log.d(TAG, "startPackageListening " + getComponent()); IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); @@ -402,7 +432,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements this, mUser, filter, null, mHandler, Context.RECEIVER_EXPORTED); } catch (Exception ex) { mPackageReceiverRegistered.set(false); - Log.e(TAG, "Could not register package receiver", ex); + Log.e(TAG, "Could not register package receiver " + getComponent(), ex); } filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); try { @@ -410,12 +440,12 @@ public class TileLifecycleManager extends BroadcastReceiver implements mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, mUser); } catch (Exception ex) { mUserReceiverRegistered.set(false); - Log.e(TAG, "Could not register unlock receiver", ex); + Log.e(TAG, "Could not register unlock receiver " + getComponent(), ex); } } private void stopPackageListening() { - if (DEBUG) Log.d(TAG, "stopPackageListening"); + if (mDebug) Log.d(TAG, "stopPackageListening " + getComponent()); if (mUserReceiverRegistered.compareAndSet(true, false)) { mBroadcastDispatcher.unregisterReceiver(this); } @@ -430,7 +460,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onReceive(Context context, Intent intent) { - if (DEBUG) Log.d(TAG, "onReceive: " + intent); + if (mDebug) Log.d(TAG, "onReceive: " + intent); if (!Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { Uri data = intent.getData(); String pkgName = data.getEncodedSchemeSpecificPart(); @@ -446,7 +476,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements if (mBound.get()) { // Trying to bind again will check the state of the package before bothering to // bind. - if (DEBUG) Log.d(TAG, "Trying to rebind"); + if (mDebug) Log.d(TAG, "Trying to rebind " + getComponent()); setBindService(true); } @@ -458,7 +488,9 @@ public class TileLifecycleManager extends BroadcastReceiver implements try { ServiceInfo si = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(), 0, mUser.getIdentifier()); - if (DEBUG && si == null) Log.d(TAG, "Can't find component " + mIntent.getComponent()); + if (mDebug && si == null) { + Log.d(TAG, "Can't find component " + mIntent.getComponent()); + } return si != null; } catch (RemoteException e) { // Shouldn't happen. @@ -472,7 +504,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements mPackageManagerAdapter.getPackageInfoAsUser(packageName, 0, mUser.getIdentifier()); return true; } catch (PackageManager.NameNotFoundException e) { - if (DEBUG) { + if (mDebug) { Log.d(TAG, "Package not available: " + packageName, e); } else { Log.d(TAG, "Package not available: " + packageName); @@ -489,7 +521,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onTileAdded() { - if (DEBUG) Log.d(TAG, "onTileAdded"); + if (mDebug) Log.d(TAG, "onTileAdded " + getComponent()); if (mWrapper == null || !mWrapper.onTileAdded()) { queueMessage(MSG_ON_ADDED); handleDeath(); @@ -498,7 +530,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onTileRemoved() { - if (DEBUG) Log.d(TAG, "onTileRemoved"); + if (mDebug) Log.d(TAG, "onTileRemoved " + getComponent()); if (mWrapper == null || !mWrapper.onTileRemoved()) { queueMessage(MSG_ON_REMOVED); handleDeath(); @@ -507,7 +539,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onStartListening() { - if (DEBUG) Log.d(TAG, "onStartListening"); + if (mDebug) Log.d(TAG, "onStartListening " + getComponent()); mListening = true; if (mWrapper != null && !mWrapper.onStartListening()) { handleDeath(); @@ -516,7 +548,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onStopListening() { - if (DEBUG) Log.d(TAG, "onStopListening"); + if (mDebug) Log.d(TAG, "onStopListening " + getComponent()); mListening = false; if (mWrapper != null && !mWrapper.onStopListening()) { handleDeath(); @@ -525,7 +557,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onClick(IBinder iBinder) { - if (DEBUG) Log.d(TAG, "onClick " + iBinder + " " + mUser); + if (mDebug) Log.d(TAG, "onClick " + iBinder + " " + getComponent() + " " + mUser); if (mWrapper == null || !mWrapper.onClick(iBinder)) { mClickBinder = iBinder; queueMessage(MSG_ON_CLICK); @@ -535,7 +567,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void onUnlockComplete() { - if (DEBUG) Log.d(TAG, "onUnlockComplete"); + if (mDebug) Log.d(TAG, "onUnlockComplete " + getComponent()); if (mWrapper == null || !mWrapper.onUnlockComplete()) { queueMessage(MSG_ON_UNLOCK_COMPLETE); handleDeath(); @@ -550,7 +582,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements @Override public void binderDied() { - if (DEBUG) Log.d(TAG, "binderDeath"); + if (mDebug) Log.d(TAG, "binderDeath " + getComponent()); handleDeath(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt index db3cf0f70f69..1805eb182cb1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt @@ -24,6 +24,7 @@ import android.view.View.AccessibilityDelegate import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction import android.widget.ImageView @@ -54,6 +55,7 @@ internal class BluetoothTileDialog constructor( private val bluetoothToggleInitialValue: Boolean, private val subtitleResIdInitialValue: Int, + private val cachedContentHeight: Int, private val bluetoothTileDialogCallback: BluetoothTileDialogCallback, @Main private val mainDispatcher: CoroutineDispatcher, private val systemClock: SystemClock, @@ -72,6 +74,11 @@ constructor( internal val deviceItemClick get() = mutableDeviceItemClick.asSharedFlow() + private val mutableContentHeight: MutableSharedFlow<Int> = + MutableSharedFlow(extraBufferCapacity = 1) + internal val contentHeight + get() = mutableContentHeight.asSharedFlow() + private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback) private var lastUiUpdateMs: Long = -1 @@ -84,6 +91,7 @@ constructor( private lateinit var seeAllButton: View private lateinit var pairNewDeviceButton: View private lateinit var deviceListView: RecyclerView + private lateinit var scrollViewContent: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -110,12 +118,23 @@ constructor( pairNewDeviceButton.setOnClickListener { bluetoothTileDialogCallback.onPairNewDeviceClicked(it) } + requireViewById<View>(R.id.scroll_view).apply { + scrollViewContent = this + layoutParams.height = cachedContentHeight + } } override fun start() { lastUiUpdateMs = systemClock.elapsedRealtime() } + override fun dismiss() { + if (::scrollViewContent.isInitialized) { + mutableContentHeight.tryEmit(scrollViewContent.measuredHeight) + } + super.dismiss() + } + internal suspend fun onDeviceItemUpdated( deviceItem: List<DeviceItem>, showSeeAll: Boolean, @@ -124,14 +143,16 @@ constructor( withContext(mainDispatcher) { val start = systemClock.elapsedRealtime() val itemRow = deviceItem.size + showSeeAll.toInt() + showPairNewDevice.toInt() - // Add a slight delay for smoother dialog height change - if (itemRow != lastItemRow) { + // If not the first load, add a slight delay for smoother dialog height change + if (itemRow != lastItemRow && lastItemRow != -1) { delay(MIN_HEIGHT_CHANGE_INTERVAL_MS - (start - lastUiUpdateMs)) } if (isActive) { deviceItemAdapter.refreshDeviceItemList(deviceItem) { seeAllButton.visibility = if (showSeeAll) VISIBLE else GONE pairNewDeviceButton.visibility = if (showPairNewDevice) VISIBLE else GONE + // Update the height after data is updated + scrollViewContent.layoutParams.height = WRAP_CONTENT lastUiUpdateMs = systemClock.elapsedRealtime() lastItemRow = itemRow logger.logDeviceUiUpdate(lastUiUpdateMs - start) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt index 5d5e747ba979..6d08f591690f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt @@ -18,14 +18,18 @@ package com.android.systemui.qs.tiles.dialog.bluetooth import android.content.Context import android.content.Intent +import android.content.SharedPreferences import android.os.Bundle import android.view.View +import android.view.ViewGroup import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger +import com.android.systemui.Prefs import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS @@ -45,6 +49,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** ViewModel for Bluetooth Dialog after clicking on the Bluetooth QS tile. */ @SysUISingleton @@ -60,6 +65,8 @@ constructor( private val logger: BluetoothTileDialogLogger, @Application private val coroutineScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, + @Background private val backgroundDispatcher: CoroutineDispatcher, + @Main private val sharedPreferences: SharedPreferences, ) : BluetoothTileDialogCallback { private var job: Job? = null @@ -145,14 +152,31 @@ constructor( .onEach { deviceItemInteractor.updateDeviceItemOnClick(it) } .launchIn(this) + dialog.contentHeight + .onEach { + withContext(backgroundDispatcher) { + sharedPreferences.edit().putInt(CONTENT_HEIGHT_PREF_KEY, it).apply() + } + } + .launchIn(this) + produce<Unit> { awaitClose { dialog.cancel() } } } } - private fun createBluetoothTileDialog(context: Context): BluetoothTileDialog { + private suspend fun createBluetoothTileDialog(context: Context): BluetoothTileDialog { + val cachedContentHeight = + withContext(backgroundDispatcher) { + sharedPreferences.getInt( + CONTENT_HEIGHT_PREF_KEY, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + return BluetoothTileDialog( bluetoothStateInteractor.isBluetoothEnabled, getSubtitleResId(bluetoothStateInteractor.isBluetoothEnabled), + cachedContentHeight, this@BluetoothTileDialogViewModel, mainDispatcher, systemClock, @@ -205,6 +229,7 @@ constructor( companion object { private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog" + private const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT private fun getSubtitleResId(isBluetoothEnabled: Boolean) = if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle else R.string.bt_is_off diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt new file mode 100644 index 000000000000..4af985424a39 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion.domain + +import android.content.res.Resources +import android.content.res.Resources.Theme +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [ColorInversionTileModel] to [QSTileState]. */ +class ColorInversionTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Theme, +) : QSTileDataToStateMapper<ColorInversionTileModel> { + override fun map(config: QSTileConfig, data: ColorInversionTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + val subtitleArray = resources.getStringArray(R.array.tile_states_inversion) + + if (data.isEnabled) { + activationState = QSTileState.ActivationState.ACTIVE + secondaryLabel = subtitleArray[2] + icon = { + Icon.Loaded( + resources.getDrawable(R.drawable.qs_invert_colors_icon_on, theme), + null + ) + } + } else { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = subtitleArray[1] + icon = { + Icon.Loaded( + resources.getDrawable(R.drawable.qs_invert_colors_icon_off, theme), + null + ) + } + } + contentDescription = label + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt new file mode 100644 index 000000000000..7f3dd3e17b79 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.os.UserHandle +import com.android.systemui.accessibility.data.repository.ColorInversionRepository +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map + +/** Observes color inversion state changes providing the [ColorInversionTileModel]. */ +class ColorInversionTileDataInteractor +@Inject +constructor( + private val colorInversionRepository: ColorInversionRepository, +) : QSTileDataInteractor<ColorInversionTileModel> { + + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<ColorInversionTileModel> { + return colorInversionRepository.isEnabled(user).map { ColorInversionTileModel(it) } + } + override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt new file mode 100644 index 000000000000..43b58c83f7ef --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.systemui.accessibility.data.repository.ColorInversionRepository +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject + +/** Handles color inversion tile clicks. */ +class ColorInversionUserActionInteractor +@Inject +constructor( + private val colorInversionRepository: ColorInversionRepository, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, +) : QSTileUserActionInteractor<ColorInversionTileModel> { + + override suspend fun handleInput(input: QSTileInput<ColorInversionTileModel>): Unit = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + colorInversionRepository.setIsEnabled( + !data.isEnabled, + user, + ) + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.view, + Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS) + ) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/model/ColorInversionTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/model/ColorInversionTileModel.kt new file mode 100644 index 000000000000..affaa6d533bf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/model/ColorInversionTileModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion.domain.model + +/** + * Color inversion tile model. + * + * @param isEnabled is true when the color inversion is enabled; + */ +@JvmInline value class ColorInversionTileModel(val isEnabled: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt new file mode 100644 index 000000000000..ab69acbc6e9d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade + +import android.content.Context +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.View +import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.ui.viewmodel.CommunalViewModel +import com.android.systemui.compose.ComposeFacade.createCommunalContainer +import com.android.systemui.compose.ComposeFacade.isComposeAvailable +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.util.kotlin.collectFlow +import javax.inject.Inject + +/** + * Controller that's responsible for the glanceable hub container view and its touch handling. + * + * This will be used until the glanceable hub is integrated into Flexiglass. + */ +class GlanceableHubContainerController +@Inject +constructor( + private val communalInteractor: CommunalInteractor, + private val communalViewModel: CommunalViewModel, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val shadeInteractor: ShadeInteractor, +) { + /** The container view for the hub. This will not be initialized until [initView] is called. */ + private lateinit var communalContainerView: View + + /** + * The width of the area in which a right edge swipe can open the hub, in pixels. Read from + * resources when [initView] is called. + */ + private var edgeSwipeRegionWidth: Int = 0 + + /** + * True if we are currently tracking a gesture for opening the hub that started in the edge + * swipe region. + */ + private var isTrackingOpenGesture = false + + /** + * True if the hub UI is fully open, meaning it should receive touch input. + * + * Tracks [CommunalInteractor.isCommunalShowing]. + */ + private var hubShowing = false + + /** + * True if either the primary or alternate bouncer are open, meaning the hub should not receive + * any touch input. + * + * Tracks [KeyguardTransitionInteractor.isFinishedInState] for [KeyguardState.isBouncerState]. + */ + private var anyBouncerShowing = false + + /** + * True if the shade is fully expanded, meaning the hub should not receive any touch input. + * + * Tracks [ShadeInteractor.isAnyFullyExpanded]. + */ + private var shadeShowing = false + + /** Returns true if the glanceable hub is enabled and the container view can be created. */ + fun isEnabled(): Boolean { + return communalInteractor.isCommunalEnabled && isComposeAvailable() + } + + /** + * Creates the container view containing the glanceable hub UI. + * + * @throws RuntimeException if [isEnabled] is false or the view is already initialized + */ + fun initView( + context: Context, + ): View { + return initView(createCommunalContainer(context, communalViewModel)) + } + + /** Override for testing. */ + @VisibleForTesting + internal fun initView(containerView: View): View { + if (!isEnabled()) { + throw RuntimeException("Glanceable hub is not enabled") + } + if (::communalContainerView.isInitialized) { + throw RuntimeException("Communal view has already been initialized") + } + + communalContainerView = containerView + + edgeSwipeRegionWidth = + communalContainerView.resources.getDimensionPixelSize(R.dimen.communal_grid_gutter_size) + + collectFlow( + communalContainerView, + keyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState), + { anyBouncerShowing = it } + ) + collectFlow( + communalContainerView, + communalInteractor.isCommunalShowing, + { hubShowing = it } + ) + collectFlow( + communalContainerView, + shadeInteractor.isAnyFullyExpanded, + { shadeShowing = it } + ) + + return communalContainerView + } + + /** + * Notifies the hub container of a touch event. Returns true if it's determined that the touch + * should go to the hub container and no one else. + * + * Special handling is needed because the hub container sits at the lowest z-order in + * [NotificationShadeWindowView] and would not normally receive touches. We also cannot use a + * [GestureDetector] as the hub container's SceneTransitionLayout is a Compose view that expects + * to be fully in control of its own touch handling. + */ + fun onTouchEvent(ev: MotionEvent): Boolean { + if (!::communalContainerView.isInitialized) { + return false + } + + val isDown = ev.actionMasked == MotionEvent.ACTION_DOWN + val isUp = ev.actionMasked == MotionEvent.ACTION_UP + val isCancel = ev.actionMasked == MotionEvent.ACTION_CANCEL + + // TODO(b/315207481): also account for opening animations of shade/bouncer and not just + // fully showing state + val hubOccluded = anyBouncerShowing || shadeShowing + + // If the hub is fully visible, send all touch events to it. + val communalVisible = hubShowing && !hubOccluded + if (communalVisible) { + return communalContainerView.dispatchTouchEvent(ev) + } + + if (edgeSwipeRegionWidth == 0) { + // If the edge region width has not been read yet or whatever reason, don't bother + // intercepting touches to open the hub. + return false + } + + if (!isTrackingOpenGesture && isDown) { + val x = ev.rawX + val inOpeningSwipeRegion: Boolean = + x >= communalContainerView.width - edgeSwipeRegionWidth + if (inOpeningSwipeRegion && !hubOccluded) { + isTrackingOpenGesture = true + return communalContainerView.dispatchTouchEvent(ev) + } + } else if (isTrackingOpenGesture) { + if (isUp || isCancel) { + isTrackingOpenGesture = false + } + return communalContainerView.dispatchTouchEvent(ev) + } + + return false + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 73537edcc94a..07ce57735dc7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -42,9 +42,6 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder; import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.classifier.FalsingCollector; -import com.android.systemui.communal.data.repository.CommunalRepository; -import com.android.systemui.communal.ui.viewmodel.CommunalViewModel; -import com.android.systemui.compose.ComposeFacade; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; @@ -108,14 +105,14 @@ public class NotificationShadeWindowViewController implements Dumpable { private final PulsingGestureListener mPulsingGestureListener; private final LockscreenHostedDreamGestureListener mLockscreenHostedDreamGestureListener; private final NotificationInsetsController mNotificationInsetsController; - private final CommunalViewModel mCommunalViewModel; - private final CommunalRepository mCommunalRepository; private final boolean mIsTrackpadCommonEnabled; private final FeatureFlagsClassic mFeatureFlagsClassic; private final SysUIKeyEventHandler mSysUIKeyEventHandler; private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final QuickSettingsController mQuickSettingsController; + private final GlanceableHubContainerController + mGlanceableHubContainerController; private GestureDetector mPulsingWakeupGestureHandler; private GestureDetector mDreamingWakeupGestureHandler; private View mBrightnessMirror; @@ -183,8 +180,7 @@ public class NotificationShadeWindowViewController implements Dumpable { KeyguardMessageAreaController.Factory messageAreaControllerFactory, KeyguardTransitionInteractor keyguardTransitionInteractor, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, - CommunalViewModel communalViewModel, - CommunalRepository communalRepository, + GlanceableHubContainerController glanceableHubContainerController, NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, FeatureFlagsClassic featureFlagsClassic, SystemClock clock, @@ -217,8 +213,7 @@ public class NotificationShadeWindowViewController implements Dumpable { mPulsingGestureListener = pulsingGestureListener; mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener; mNotificationInsetsController = notificationInsetsController; - mCommunalViewModel = communalViewModel; - mCommunalRepository = communalRepository; + mGlanceableHubContainerController = glanceableHubContainerController; mIsTrackpadCommonEnabled = featureFlagsClassic.isEnabled(TRACKPAD_GESTURE_COMMON); mFeatureFlagsClassic = featureFlagsClassic; mSysUIKeyEventHandler = sysUIKeyEventHandler; @@ -347,6 +342,10 @@ public class NotificationShadeWindowViewController implements Dumpable { mFalsingCollector.onTouchEvent(ev); mPulsingWakeupGestureHandler.onTouchEvent(ev); + + if (mGlanceableHubContainerController.onTouchEvent(ev)) { + return logDownDispatch(ev, "dispatched to glanceable hub container", true); + } if (mDreamingWakeupGestureHandler != null && mDreamingWakeupGestureHandler.onTouchEvent(ev)) { return logDownDispatch(ev, "dream wakeup gesture handled", true); @@ -587,14 +586,13 @@ public class NotificationShadeWindowViewController implements Dumpable { } /** - * Sets up the communal hub UI if the {@link com.android.systemui.Flags#FLAG_COMMUNAL_HUB} flag - * is enabled. + * Sets up the glanceable hub UI if the {@link com.android.systemui.Flags#FLAG_COMMUNAL_HUB} + * flag is enabled. * - * The layout lives in {@link R.id.communal_ui_container}. + * The layout lives in {@link R.id.communal_ui_stub}. */ public void setupCommunalHubLayout() { - if (!mCommunalRepository.isCommunalEnabled() - || !ComposeFacade.INSTANCE.isComposeAvailable()) { + if (!mGlanceableHubContainerController.isEnabled()) { return; } @@ -602,8 +600,8 @@ public class NotificationShadeWindowViewController implements Dumpable { View communalPlaceholder = mView.findViewById(R.id.communal_ui_stub); int index = mView.indexOfChild(communalPlaceholder); mView.removeView(communalPlaceholder); - mView.addView(ComposeFacade.INSTANCE.createCommunalContainer(mView.getContext(), - mCommunalViewModel), index); + + mView.addView(mGlanceableHubContainerController.initView(mView.getContext()), index); } private boolean didNotificationPanelInterceptEvent(MotionEvent ev) { diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt index d26fded19cc1..2f58b35ae182 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt @@ -41,14 +41,14 @@ abstract class SmartspaceModule { const val DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN = "dream_weather_smartspace_data_plugin" /** - * The dream smartspace target filter. + * The target filter for smartspace over lockscreen. */ - const val DREAM_SMARTSPACE_TARGET_FILTER = "dream_smartspace_target_filter" + const val LOCKSCREEN_SMARTSPACE_TARGET_FILTER = "lockscreen_smartspace_target_filter" /** - * The precondition for dream smartspace + * The precondition for smartspace over lockscreen */ - const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition" + const val LOCKSCREEN_SMARTSPACE_PRECONDITION = "lockscreen_smartspace_precondition" /** * The BcSmartspaceDataPlugin for the standalone date (+alarm+dnd). @@ -67,7 +67,7 @@ abstract class SmartspaceModule { } @BindsOptionalOf - @Named(DREAM_SMARTSPACE_TARGET_FILTER) + @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER) abstract fun optionalDreamSmartspaceTargetFilter(): SmartspaceTargetFilter? @BindsOptionalOf @@ -79,7 +79,7 @@ abstract class SmartspaceModule { abstract fun optionalDreamWeatherSmartspaceDataPlugin(): BcSmartspaceDataPlugin? @Binds - @Named(DREAM_SMARTSPACE_PRECONDITION) + @Named(LOCKSCREEN_SMARTSPACE_PRECONDITION) abstract fun bindSmartspacePrecondition( lockscreenPrecondition: LockscreenPrecondition? ): SmartspacePrecondition? diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt index aa4cf75a7a6e..5fb85e59ad91 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt @@ -31,10 +31,10 @@ import com.android.systemui.util.settings.SecureSettings import java.util.concurrent.Executor import javax.inject.Inject -/** - * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen and dreams. - */ -class LockscreenAndDreamTargetFilter @Inject constructor( +/** {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen. */ +class LockscreenTargetFilter +@Inject +constructor( private val secureSettings: SecureSettings, private val userTracker: UserTracker, private val execution: Execution, @@ -60,12 +60,13 @@ class LockscreenAndDreamTargetFilter @Inject constructor( } } - private val settingsObserver = object : ContentObserver(handler) { - override fun onChange(selfChange: Boolean, uri: Uri?) { - execution.assertIsMainThread() - updateUserContentSettings() + private val settingsObserver = + object : ContentObserver(handler) { + override fun onChange(selfChange: Boolean, uri: Uri?) { + execution.assertIsMainThread() + updateUserContentSettings() + } } - } private var managedUserHandle: UserHandle? = null @@ -79,10 +80,10 @@ class LockscreenAndDreamTargetFilter @Inject constructor( userTracker.addCallback(userTrackerCallback, uiExecutor) contentResolver.registerContentObserver( - secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), - true, - settingsObserver, - UserHandle.USER_ALL + secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + true, + settingsObserver, + UserHandle.USER_ALL ) updateUserContentSettings() @@ -110,7 +111,7 @@ class LockscreenAndDreamTargetFilter @Inject constructor( // Only the primary user can have an associated managed profile, so only show // content for the managed profile if the primary user is active userTracker.userHandle.identifier == UserHandle.USER_SYSTEM && - (!t.isSensitive || showSensitiveContentForManagedUser) + (!t.isSensitive || showSensitiveContentForManagedUser) } else -> { false @@ -118,12 +119,13 @@ class LockscreenAndDreamTargetFilter @Inject constructor( } } - private val userTrackerCallback = object : UserTracker.Callback { - override fun onUserChanged(newUser: Int, userContext: Context) { - execution.assertIsMainThread() - updateUserContentSettings() + private val userTrackerCallback = + object : UserTracker.Callback { + override fun onUserChanged(newUser: Int, userContext: Context) { + execution.assertIsMainThread() + updateUserContentSettings() + } } - } private fun getWorkProfileUser(): UserHandle? { for (userInfo in userTracker.userProfiles) { @@ -138,13 +140,13 @@ class LockscreenAndDreamTargetFilter @Inject constructor( val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS showSensitiveContentForCurrentUser = - secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1 + secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1 managedUserHandle = getWorkProfileUser() val managedId = managedUserHandle?.identifier if (managedId != null) { showSensitiveContentForManagedUser = - secureSettings.getIntForUser(setting, 0, managedId) == 1 + secureSettings.getIntForUser(setting, 0, managedId) == 1 } listeners.forEach { it.onCriteriaChanged() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index b6d4dedfe6f7..5872840913f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -38,6 +38,7 @@ import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.os.SystemProperties; import android.os.Trace; import android.util.AttributeSet; import android.util.FloatProperty; @@ -272,6 +273,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private final RefactorFlag mInlineReplyAnimation = RefactorFlag.forView(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION); + private static final boolean mSimulateSlowMeasure = Compile.IS_DEBUG && RefactorFlag.forView( + Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE).isEnabled(); + private static final String SLOW_MEASURE_SIMULATE_DELAY_PROPERTY = + "persist.notifications.extra_measure_delay_ms"; + private static final int SLOW_MEASURE_SIMULATE_DELAY_MS = mSimulateSlowMeasure ? + SystemProperties.getInt(SLOW_MEASURE_SIMULATE_DELAY_PROPERTY, 150) : 0; + // Listener will be called when receiving a long click event. // Use #setLongPressPosition to optionally assign positional data with the long press. private LongPressListener mLongPressListener; @@ -1879,9 +1887,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView + "heightMeasureSpec=" + MeasureSpec.toString(heightMeasureSpec) + ")"); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (Compile.IS_DEBUG && mSimulateSlowMeasure) { + simulateExtraMeasureDelay(); + } Trace.endSection(); } + private void simulateExtraMeasureDelay() { + // Add extra delay in a notification row instead of NotificationStackScrollLayout + // to make sure that when the measure cache is used we won't add this delay + try { + Trace.beginSection("ExtraDebugMeasureDelay"); + Thread.sleep(SLOW_MEASURE_SIMULATE_DELAY_MS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + Trace.endSection(); + } + } + /** * Generates and appends "(MessagingStyle)" type tag to passed string for tracing. */ diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index fdf5966419b4..20fef927e8d1 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -317,10 +317,11 @@ public class ImageWallpaper extends WallpaperService { } private void loadWallpaperAndDrawFrameInternal() { - Trace.beginSection("ImageWallpaper.CanvasEngine#loadWallpaper"); + Trace.beginSection("WPMS.ImageWallpaper.CanvasEngine#loadWallpaper"); boolean loadSuccess = false; Bitmap bitmap; try { + Trace.beginSection("WPMS.getBitmapAsUser"); bitmap = mWallpaperManager.getBitmapAsUser( mUserTracker.getUserId(), false, getSourceFlag(), true); if (bitmap != null @@ -333,15 +334,22 @@ public class ImageWallpaper extends WallpaperService { // be loaded, we will go into a cycle. Don't do a build where the // default wallpaper can't be loaded. Log.w(TAG, "Unable to load wallpaper!", exception); + Trace.beginSection("WPMS.clearWallpaper"); mWallpaperManager.clearWallpaper(getWallpaperFlags(), mUserTracker.getUserId()); + Trace.endSection(); try { + Trace.beginSection("WPMS.getBitmapAsUser_defaultWallpaper"); bitmap = mWallpaperManager.getBitmapAsUser( mUserTracker.getUserId(), false, getSourceFlag(), true); } catch (RuntimeException | OutOfMemoryError e) { Log.w(TAG, "Unable to load default wallpaper!", e); bitmap = null; + } finally { + Trace.endSection(); } + } finally { + Trace.endSection(); } if (bitmap == null) { @@ -355,15 +363,23 @@ public class ImageWallpaper extends WallpaperService { loadSuccess = true; // recycle the previously loaded bitmap if (mBitmap != null) { + Trace.beginSection("WPMS.mBitmap.recycle"); mBitmap.recycle(); + Trace.endSection(); } mBitmap = bitmap; + Trace.beginSection("WPMS.wallpaperSupportsWcg"); mWideColorGamut = mWallpaperManager.wallpaperSupportsWcg(getSourceFlag()); + Trace.endSection(); // +2 usages for the color extraction and the delayed unload. mBitmapUsages += 2; + Trace.beginSection("WPMS.recomputeColorExtractorMiniBitmap"); recomputeColorExtractorMiniBitmap(); + Trace.endSection(); + Trace.beginSection("WPMS.drawFrameInternal"); drawFrameInternal(); + Trace.endSection(); /* * after loading, the bitmap will be unloaded after all these conditions: diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt index b1421b21b377..e921a59f5860 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt @@ -9,30 +9,34 @@ import android.util.PathParser import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Expect import java.util.concurrent.Executor import kotlin.math.roundToInt -import kotlin.test.assertEquals -import kotlin.test.assertNotNull import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @SmallTest class CameraAvailabilityListenerTest : SysuiTestCase() { companion object { - const val EXCLUDED_PKG = "test.excluded.package" - const val CAMERA_ID_FRONT = "0" - const val CAMERA_ID_INNER = "1" - const val PROTECTION_PATH_STRING_FRONT = "M 50,50 a 20,20 0 1 0 40,0 a 20,20 0 1 0 -40,0 Z" - const val PROTECTION_PATH_STRING_INNER = "M 40,40 a 10,10 0 1 0 20,0 a 10,10 0 1 0 -20,0 Z" - val PATH_RECT_FRONT = rectFromPath(pathFromString(PROTECTION_PATH_STRING_FRONT)) - val PATH_RECT_INNER = rectFromPath(pathFromString(PROTECTION_PATH_STRING_INNER)) + private const val EXCLUDED_PKG = "test.excluded.package" + private const val LOGICAL_CAMERA_ID_NOT_SPECIFIED = "" + private const val LOGICAL_CAMERA_ID_REAR = "0" + private const val LOGICAL_CAMERA_ID_FRONT = "1" + private const val PHYSICAL_CAMERA_ID_OUTER_FRONT = "5" + private const val PHYSICAL_CAMERA_ID_INNER_FRONT = "6" + private const val PROTECTION_PATH_STRING_OUTER_FRONT = + "M 50,50 a 20,20 0 1 0 40,0 a 20,20 0 1 0 -40,0 Z" + private const val PROTECTION_PATH_STRING_INNER_FRONT = + "M 40,40 a 10,10 0 1 0 20,0 a 10,10 0 1 0 -20,0 Z" + private val PATH_RECT_FRONT = + rectFromPath(pathFromString(PROTECTION_PATH_STRING_OUTER_FRONT)) + private val PATH_RECT_INNER = + rectFromPath(pathFromString(PROTECTION_PATH_STRING_INNER_FRONT)) private fun pathFromString(pathString: String): Path { val spec = pathString.trim() @@ -58,105 +62,310 @@ class CameraAvailabilityListenerTest : SysuiTestCase() { } } - @Mock private lateinit var cameraManager: CameraManager - @Mock - private lateinit var cameraTransitionCb: CameraAvailabilityListener.CameraTransitionCallback - private lateinit var cameraAvailabilityListener: CameraAvailabilityListener + @get:Rule val expect: Expect = Expect.create() + + private val cameraManager = mock<CameraManager>() + private val cameraTransitionCallback = TestCameraTransitionCallback() + + private lateinit var cameraAvailabilityCallback: CameraManager.AvailabilityCallback @Before fun setUp() { - MockitoAnnotations.initMocks(this) - context - .getOrCreateTestableResources() - .addOverride(R.string.config_cameraProtectionExcludedPackages, EXCLUDED_PKG) - context - .getOrCreateTestableResources() - .addOverride(R.string.config_protectedCameraId, CAMERA_ID_FRONT) - context - .getOrCreateTestableResources() - .addOverride( - R.string.config_frontBuiltInDisplayCutoutProtection, - PROTECTION_PATH_STRING_FRONT - ) - context - .getOrCreateTestableResources() - .addOverride(R.string.config_protectedInnerCameraId, CAMERA_ID_INNER) - context - .getOrCreateTestableResources() - .addOverride( - R.string.config_innerBuiltInDisplayCutoutProtection, - PROTECTION_PATH_STRING_INNER + overrideResource(R.string.config_cameraProtectionExcludedPackages, EXCLUDED_PKG) + setOuterFrontCameraId(LOGICAL_CAMERA_ID_FRONT) + setOuterFrontPhysicalCameraId(PHYSICAL_CAMERA_ID_OUTER_FRONT) + overrideResource( + R.string.config_frontBuiltInDisplayCutoutProtection, + PROTECTION_PATH_STRING_OUTER_FRONT + ) + setInnerFrontCameraId(LOGICAL_CAMERA_ID_FRONT) + setInnerFrontPhysicalCameraId(PHYSICAL_CAMERA_ID_INNER_FRONT) + overrideResource( + R.string.config_innerBuiltInDisplayCutoutProtection, + PROTECTION_PATH_STRING_INNER_FRONT + ) + context.addMockSystemService(CameraManager::class.java, cameraManager) + + whenever( + cameraManager.registerAvailabilityCallback( + any(Executor::class.java), + any(CameraManager.AvailabilityCallback::class.java) + ) ) + .thenAnswer { + cameraAvailabilityCallback = it.arguments[1] as CameraManager.AvailabilityCallback + return@thenAnswer Unit + } + } - context.addMockSystemService(CameraManager::class.java, cameraManager) + @Test + fun onCameraOpened_matchesOuterFrontInfo_showsOuterProtection() { + setInnerFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() - cameraAvailabilityListener = - CameraAvailabilityListener.Factory.build(context, context.mainExecutor) + openCamera(LOGICAL_CAMERA_ID_FRONT) + + assertOuterProtectionShowing() } @Test - fun testFrontCamera() { - var path: Path? = null - var rect: Rect? = null - val callback = - object : CameraAvailabilityListener.CameraTransitionCallback { - override fun onApplyCameraProtection(protectionPath: Path, bounds: Rect) { - path = protectionPath - rect = bounds - } + fun onCameraOpened_matchesInnerFrontInfo_showsInnerProtection() { + setOuterFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() - override fun onHideCameraProtection() {} - } + openCamera(LOGICAL_CAMERA_ID_FRONT) - cameraAvailabilityListener.addTransitionCallback(callback) - cameraAvailabilityListener.startListening() + assertInnerProtectionShowing() + } - val callbackCaptor = withArgCaptor { - verify(cameraManager).registerAvailabilityCallback(any(Executor::class.java), capture()) - } + @Test + fun onCameraOpened_doesNotMatchAnyProtectionInfo_doesNotShowProtection() { + createAndStartSut() - callbackCaptor.onCameraOpened(CAMERA_ID_FRONT, "") - assertNotNull(path) - assertEquals(PATH_RECT_FRONT, rect) + openCamera(LOGICAL_CAMERA_ID_REAR) + + assertProtectionNotShowing() } @Test - fun testInnerCamera() { - var path: Path? = null - var rect: Rect? = null - val callback = - object : CameraAvailabilityListener.CameraTransitionCallback { - override fun onApplyCameraProtection(protectionPath: Path, bounds: Rect) { - path = protectionPath - rect = bounds - } + fun onCameraOpened_matchesInnerAndOuter_innerUnavailable_showsOuterProtection() { + val dupeCameraId = "1" + setInnerFrontCameraId(dupeCameraId) + setOuterFrontCameraId(dupeCameraId) + createAndStartSut() - override fun onHideCameraProtection() {} - } + setPhysicalCameraUnavailable(dupeCameraId, PHYSICAL_CAMERA_ID_INNER_FRONT) + openCamera(dupeCameraId) - cameraAvailabilityListener.addTransitionCallback(callback) - cameraAvailabilityListener.startListening() + assertOuterProtectionShowing() + } - val callbackCaptor = withArgCaptor { - verify(cameraManager).registerAvailabilityCallback(any(Executor::class.java), capture()) - } + @Test + fun onCameraOpened_matchesInnerAndOuter_outerUnavailable_showsInnerFrontProtection() { + val dupeCameraId = "1" + setInnerFrontCameraId(dupeCameraId) + setOuterFrontCameraId(dupeCameraId) + createAndStartSut() + + setPhysicalCameraUnavailable(dupeCameraId, PHYSICAL_CAMERA_ID_OUTER_FRONT) + openCamera(dupeCameraId) - callbackCaptor.onCameraOpened(CAMERA_ID_INNER, "") - assertNotNull(path) - assertEquals(PATH_RECT_INNER, rect) + assertInnerProtectionShowing() } @Test - fun testExcludedPackage() { - cameraAvailabilityListener.addTransitionCallback(cameraTransitionCb) - cameraAvailabilityListener.startListening() + fun onCameraOpened_matchesOuterFrontInfo_packageExcluded_doesNotShowProtection() { + setInnerFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + + openCamera(LOGICAL_CAMERA_ID_FRONT, EXCLUDED_PKG) + + assertProtectionNotShowing() + } + + @Test + fun onCameraOpened_matchesInnerFrontInfo_packageExcluded_doesNotShowProtection() { + setOuterFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + + openCamera(LOGICAL_CAMERA_ID_FRONT, EXCLUDED_PKG) + + assertProtectionNotShowing() + } + + @Test + fun onCameraClosed_matchesActiveOuterFrontProtection_hidesProtection() { + setInnerFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + openCamera(LOGICAL_CAMERA_ID_FRONT) + + closeCamera(LOGICAL_CAMERA_ID_FRONT) + + assertProtectionNotShowing() + } + + @Test + fun onCameraClosed_matchesActiveInnerFrontProtection_hidesProtection() { + setOuterFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + openCamera(LOGICAL_CAMERA_ID_FRONT) + + closeCamera(LOGICAL_CAMERA_ID_FRONT) + + assertProtectionNotShowing() + } + + @Test + fun onCameraClosed_doesNotMatchActiveOuterFrontProtection_keepsShowingProtection() { + setInnerFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + openCamera(LOGICAL_CAMERA_ID_FRONT) + + closeCamera(LOGICAL_CAMERA_ID_REAR) + + assertOuterProtectionShowing() + } + + @Test + fun onCameraClosed_doesNotMatchActiveInnerFrontProtection_keepsShowingProtection() { + setOuterFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + openCamera(LOGICAL_CAMERA_ID_FRONT) + + closeCamera(LOGICAL_CAMERA_ID_REAR) + + assertInnerProtectionShowing() + } + + @Test + fun onPhysicalCameraAvailable_cameraOpen_matchesOuterFront_showsOuterFrontProtection() { + val logicalCameraId = "1" + setOuterFrontCameraId(logicalCameraId) + setInnerFrontCameraId(logicalCameraId) + createAndStartSut() + setPhysicalCameraUnavailable(logicalCameraId, PHYSICAL_CAMERA_ID_OUTER_FRONT) + openCamera(logicalCameraId) + + setPhysicalCameraAvailable(logicalCameraId, PHYSICAL_CAMERA_ID_OUTER_FRONT) + + assertOuterProtectionShowing() + } - val callbackCaptor = withArgCaptor { - verify(cameraManager).registerAvailabilityCallback(any(Executor::class.java), capture()) + @Test + fun onPhysicalCameraAvailable_cameraClosed_matchesOuterFront_doesNotShowProtection() { + val logicalCameraId = "1" + setOuterFrontCameraId(logicalCameraId) + setInnerFrontCameraId(logicalCameraId) + createAndStartSut() + setPhysicalCameraUnavailable(logicalCameraId, PHYSICAL_CAMERA_ID_OUTER_FRONT) + + setPhysicalCameraAvailable(logicalCameraId, PHYSICAL_CAMERA_ID_OUTER_FRONT) + + assertProtectionNotShowing() + } + + @Test + fun onPhysicalCameraAvailable_cameraOpen_matchesInnerFront_showsInnerFrontProtection() { + val logicalCameraId = "1" + setOuterFrontCameraId(logicalCameraId) + setInnerFrontCameraId(logicalCameraId) + createAndStartSut() + setPhysicalCameraUnavailable(logicalCameraId, PHYSICAL_CAMERA_ID_INNER_FRONT) + openCamera(logicalCameraId) + + setPhysicalCameraAvailable(logicalCameraId, PHYSICAL_CAMERA_ID_INNER_FRONT) + + assertInnerProtectionShowing() + } + + @Test + fun onPhysicalCameraAvailable_cameraClosed_matchesInnerFront_doesNotShowProtection() { + val logicalCameraId = "1" + setOuterFrontCameraId(logicalCameraId) + setInnerFrontCameraId(logicalCameraId) + createAndStartSut() + setPhysicalCameraUnavailable(logicalCameraId, PHYSICAL_CAMERA_ID_INNER_FRONT) + + setPhysicalCameraAvailable(logicalCameraId, PHYSICAL_CAMERA_ID_INNER_FRONT) + + assertProtectionNotShowing() + } + + @Test + fun onPhysicalCameraUnavailable_matchesActiveProtection_hidesProtection() { + setInnerFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + openCamera(LOGICAL_CAMERA_ID_FRONT) + + setPhysicalCameraUnavailable(LOGICAL_CAMERA_ID_FRONT, PHYSICAL_CAMERA_ID_OUTER_FRONT) + + assertProtectionNotShowing() + } + + @Test + fun onPhysicalCameraUnavailable_doesNotMatchActiveProtection_keepsShowingProtection() { + setInnerFrontCameraId(LOGICAL_CAMERA_ID_NOT_SPECIFIED) + createAndStartSut() + openCamera(LOGICAL_CAMERA_ID_FRONT) + + setPhysicalCameraUnavailable(LOGICAL_CAMERA_ID_FRONT, PHYSICAL_CAMERA_ID_INNER_FRONT) + + assertOuterProtectionShowing() + } + + private fun openCamera(logicalCameraId: String, packageId: String = "") { + cameraAvailabilityCallback.onCameraOpened(logicalCameraId, packageId) + } + + private fun closeCamera(logicalCameraId: String) { + cameraAvailabilityCallback.onCameraClosed(logicalCameraId) + } + + private fun setPhysicalCameraAvailable(logicalCameraId: String, physicalCameraId: String) { + cameraAvailabilityCallback.onPhysicalCameraAvailable(logicalCameraId, physicalCameraId) + } + + private fun setPhysicalCameraUnavailable(logicalCameraId: String, physicalCameraId: String) { + cameraAvailabilityCallback.onPhysicalCameraUnavailable(logicalCameraId, physicalCameraId) + } + + private fun assertOuterProtectionShowing() { + expect.that(cameraTransitionCallback.shouldShowProtection).isTrue() + expect.that(cameraTransitionCallback.protectionPath).isNotNull() + expect.that(cameraTransitionCallback.protectionBounds).isEqualTo(PATH_RECT_FRONT) + } + + private fun assertInnerProtectionShowing() { + expect.that(cameraTransitionCallback.shouldShowProtection).isTrue() + expect.that(cameraTransitionCallback.protectionPath).isNotNull() + expect.that(cameraTransitionCallback.protectionBounds).isEqualTo(PATH_RECT_INNER) + } + + private fun assertProtectionNotShowing() { + expect.that(cameraTransitionCallback.shouldShowProtection).isFalse() + expect.that(cameraTransitionCallback.protectionBounds).isNull() + expect.that(cameraTransitionCallback.protectionPath).isNull() + } + + private fun setOuterFrontCameraId(id: String) { + overrideResource(R.string.config_protectedCameraId, id) + } + + private fun setOuterFrontPhysicalCameraId(id: String) { + overrideResource(R.string.config_protectedPhysicalCameraId, id) + } + + private fun setInnerFrontCameraId(id: String) { + overrideResource(R.string.config_protectedInnerCameraId, id) + } + + private fun setInnerFrontPhysicalCameraId(id: String) { + overrideResource(R.string.config_protectedInnerPhysicalCameraId, id) + } + + private fun createAndStartSut(): CameraAvailabilityListener { + return CameraAvailabilityListener.build(context, context.mainExecutor).also { + it.addTransitionCallback(cameraTransitionCallback) + it.startListening() + } + } + + private class TestCameraTransitionCallback : + CameraAvailabilityListener.CameraTransitionCallback { + var shouldShowProtection = false + var protectionPath: Path? = null + var protectionBounds: Rect? = null + + override fun onApplyCameraProtection(protectionPath: Path, bounds: Rect) { + shouldShowProtection = true + this.protectionPath = protectionPath + this.protectionBounds = bounds } - callbackCaptor.onCameraOpened(CAMERA_ID_FRONT, EXCLUDED_PKG) - verify(cameraTransitionCb, never()) - .onApplyCameraProtection(any(Path::class.java), any(Rect::class.java)) + override fun onHideCameraProtection() { + shouldShowProtection = false + protectionPath = null + protectionBounds = null + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java index fbd63c6bbdae..81424565daee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java @@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -300,8 +301,10 @@ public class TileLifecycleManagerTest extends SysuiTestCase { mStateManager.onStartListening(); mStateManager.executeSetBindService(true); mExecutor.runAllReady(); - mStateManager.onServiceDisconnected(mTileServiceComponentName); + mStateManager.onBindingDied(mTileServiceComponentName); + mExecutor.runAllReady(); mClock.advanceTime(5000); + mExecutor.runAllReady(); // Two calls: one for the first bind, one for the restart. verifyBind(2); @@ -318,20 +321,66 @@ public class TileLifecycleManagerTest extends SysuiTestCase { mStateManager.onStartListening(); mStateManager.executeSetBindService(true); mExecutor.runAllReady(); - mStateManager.onServiceDisconnected(mTileServiceComponentName); + verify(mMockTileService, times(1)).onStartListening(); + mStateManager.onBindingDied(mTileServiceComponentName); + mExecutor.runAllReady(); // Longer delay than a regular one mClock.advanceTime(5000); - verifyBind(1); - verify(mMockTileService, times(1)).onStartListening(); + mExecutor.runAllReady(); + + assertFalse(mContext.isBound(mTileServiceComponentName)); mClock.advanceTime(20000); + mExecutor.runAllReady(); // Two calls: one for the first bind, one for the restart. verifyBind(2); verify(mMockTileService, times(2)).onStartListening(); } @Test + public void testOnServiceDisconnectedDoesnUnbind_doesntForwardToBinder() throws Exception { + mStateManager.executeSetBindService(true); + mExecutor.runAllReady(); + + mStateManager.onStartListening(); + verify(mMockTileService).onStartListening(); + + clearInvocations(mMockTileService); + mStateManager.onServiceDisconnected(mTileServiceComponentName); + mExecutor.runAllReady(); + + mStateManager.onStartListening(); + verify(mMockTileService, never()).onStartListening(); + } + + @Test + public void testKillProcessLowMemory_unbound_doesntBindAgain() throws Exception { + doAnswer(invocation -> { + ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0); + memoryInfo.lowMemory = true; + return null; + }).when(mActivityManager).getMemoryInfo(any()); + mStateManager.onStartListening(); + mStateManager.executeSetBindService(true); + mExecutor.runAllReady(); + verifyBind(1); + verify(mMockTileService, times(1)).onStartListening(); + + mStateManager.onBindingDied(mTileServiceComponentName); + mExecutor.runAllReady(); + + clearInvocations(mMockTileService); + mStateManager.executeSetBindService(false); + mExecutor.runAllReady(); + mClock.advanceTime(30000); + mExecutor.runAllReady(); + + verifyBind(0); + verify(mMockTileService, never()).onStartListening(); + } + + @Test public void testToggleableTile() throws Exception { assertTrue(mStateManager.isToggleableTile()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt index 313ccb8a8717..154aa1cabb0c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt @@ -23,6 +23,8 @@ import android.view.LayoutInflater import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.test.filters.SmallTest @@ -54,6 +56,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { const val DEVICE_NAME = "device" const val DEVICE_CONNECTION_SUMMARY = "active" const val ENABLED = true + const val CONTENT_HEIGHT = WRAP_CONTENT } @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() @@ -88,6 +91,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -116,9 +120,9 @@ class BluetoothTileDialogTest : SysuiTestCase() { assertThat(bluetoothTileDialog.isShowing).isTrue() assertThat(recyclerView).isNotNull() - assertThat(recyclerView?.visibility).isEqualTo(VISIBLE) - assertThat(recyclerView?.adapter).isNotNull() - assertThat(recyclerView?.layoutManager is LinearLayoutManager).isTrue() + assertThat(recyclerView.visibility).isEqualTo(VISIBLE) + assertThat(recyclerView.adapter).isNotNull() + assertThat(recyclerView.layoutManager is LinearLayoutManager).isTrue() } @Test @@ -128,6 +132,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -144,7 +149,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { ) val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list) - val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter + val adapter = recyclerView.adapter as BluetoothTileDialog.Adapter assertThat(adapter.itemCount).isEqualTo(1) assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME) assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY) @@ -162,6 +167,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -189,6 +195,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -213,6 +220,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -232,13 +240,38 @@ class BluetoothTileDialogTest : SysuiTestCase() { val pairNewButton = bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_button) val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list) - val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter + val adapter = recyclerView.adapter as BluetoothTileDialog.Adapter + val scrollViewContent = bluetoothTileDialog.requireViewById<View>(R.id.scroll_view) assertThat(seeAllButton).isNotNull() assertThat(seeAllButton.visibility).isEqualTo(GONE) assertThat(pairNewButton).isNotNull() assertThat(pairNewButton.visibility).isEqualTo(VISIBLE) assertThat(adapter.itemCount).isEqualTo(1) + assertThat(scrollViewContent.layoutParams.height).isEqualTo(WRAP_CONTENT) + } + } + + @Test + fun testShowDialog_displayFromCachedHeight() { + testScope.runTest { + bluetoothTileDialog = + BluetoothTileDialog( + ENABLED, + subtitleResId, + MATCH_PARENT, + bluetoothTileDialogCallback, + dispatcher, + fakeSystemClock, + uiEventLogger, + logger, + mContext + ) + bluetoothTileDialog.show() + assertThat( + bluetoothTileDialog.requireViewById<View>(R.id.scroll_view).layoutParams.height + ) + .isEqualTo(MATCH_PARENT) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt index 99993f2b3eff..33066d2092b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.dialog.bluetooth +import android.content.SharedPreferences import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View @@ -78,6 +79,8 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { @Mock private lateinit var logger: BluetoothTileDialogLogger + @Mock private lateinit var sharedPreferences: SharedPreferences + private lateinit var scheduler: TestCoroutineScheduler private lateinit var dispatcher: CoroutineDispatcher private lateinit var testScope: TestScope @@ -98,6 +101,8 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { logger, testScope.backgroundScope, dispatcher, + dispatcher, + sharedPreferences, ) `when`(deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow()) `when`(bluetoothStateInteractor.bluetoothStateUpdate) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt new file mode 100644 index 000000000000..5569ca9520e9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.testing.ViewUtils +import android.view.MotionEvent +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.FakeCommunalRepository +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory +import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.ui.viewmodel.CommunalViewModel +import com.android.systemui.compose.ComposeFacade +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow +import org.junit.Assert.assertThrows +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.BeforeClass +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class GlanceableHubContainerControllerTest : SysuiTestCase() { + @Mock private lateinit var communalViewModel: CommunalViewModel + @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + @Mock private lateinit var shadeInteractor: ShadeInteractor + + private lateinit var containerView: View + private lateinit var testableLooper: TestableLooper + + private lateinit var communalInteractor: CommunalInteractor + private lateinit var communalRepository: FakeCommunalRepository + private lateinit var underTest: GlanceableHubContainerController + + private val bouncerShowingFlow = MutableStateFlow(false) + private val shadeShowingFlow = MutableStateFlow(false) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + val withDeps = CommunalInteractorFactory.create() + communalInteractor = withDeps.communalInteractor + communalRepository = withDeps.communalRepository + + underTest = + GlanceableHubContainerController( + communalInteractor, + communalViewModel, + keyguardTransitionInteractor, + shadeInteractor + ) + testableLooper = TestableLooper.get(this) + + communalRepository.setIsCommunalEnabled(true) + + whenever(keyguardTransitionInteractor.isFinishedInStateWhere(any())) + .thenReturn(bouncerShowingFlow) + whenever(shadeInteractor.isAnyFullyExpanded).thenReturn(shadeShowingFlow) + + overrideResource(R.dimen.communal_grid_gutter_size, SWIPE_REGION_WIDTH) + } + + @Test + fun isEnabled_interactorEnabled_returnsTrue() { + communalRepository.setIsCommunalEnabled(true) + + assertThat(underTest.isEnabled()).isTrue() + } + + @Test + fun isEnabled_interactorDisabled_returnsFalse() { + communalRepository.setIsCommunalEnabled(false) + + assertThat(underTest.isEnabled()).isFalse() + } + + @Test + fun initView_notEnabled_throwsException() { + communalRepository.setIsCommunalEnabled(false) + + assertThrows(RuntimeException::class.java) { underTest.initView(context) } + } + + @Test + fun initView_calledTwice_throwsException() { + // First call succeeds. + underTest.initView(context) + + // Second call throws. + assertThrows(RuntimeException::class.java) { underTest.initView(context) } + } + + @Test + fun onTouchEvent_touchInsideGestureRegion_returnsTrue() { + // Communal is open. + communalRepository.setDesiredScene(CommunalSceneKey.Communal) + + initAndAttachContainerView() + + // Touch events are intercepted. + assertThat(underTest.onTouchEvent(DOWN_IN_SWIPE_REGION_EVENT)).isTrue() + } + + @Test + fun onTouchEvent_subsequentTouchesAfterGestureStart_returnsTrue() { + // Communal is open. + communalRepository.setDesiredScene(CommunalSceneKey.Communal) + + initAndAttachContainerView() + + // Initial touch down is intercepted, and so are touches outside of the region, until an up + // event is received. + assertThat(underTest.onTouchEvent(DOWN_IN_SWIPE_REGION_EVENT)).isTrue() + assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue() + assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue() + assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse() + } + + @Test + fun onTouchEvent_communalOpen_returnsTrue() { + // Communal is open. + communalRepository.setDesiredScene(CommunalSceneKey.Communal) + + initAndAttachContainerView() + testableLooper.processAllMessages() + + // Touch events are intercepted. + assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue() + } + + @Test + fun onTouchEvent_communalAndBouncerShowing_returnsFalse() { + // Communal is open. + communalRepository.setDesiredScene(CommunalSceneKey.Communal) + + initAndAttachContainerView() + + // Bouncer is visible. + bouncerShowingFlow.value = true + testableLooper.processAllMessages() + + // Touch events are not intercepted. + assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + } + + @Test + fun onTouchEvent_communalAndShadeShowing_returnsFalse() { + // Communal is open. + communalRepository.setDesiredScene(CommunalSceneKey.Communal) + + initAndAttachContainerView() + + shadeShowingFlow.value = true + testableLooper.processAllMessages() + + // Touch events are not intercepted. + assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + } + + private fun initAndAttachContainerView() { + containerView = View(context) + // Make view clickable so that dispatchTouchEvent returns true. + containerView.isClickable = true + + underTest.initView(containerView) + // Attach the view so that flows start collecting. + ViewUtils.attachView(containerView) + // Give the view a size so that determining if a touch starts at the right edge works. + containerView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT) + } + + companion object { + private const val CONTAINER_WIDTH = 100 + private const val CONTAINER_HEIGHT = 100 + private const val SWIPE_REGION_WIDTH = 20 + + private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) + private val DOWN_IN_SWIPE_REGION_EVENT = + MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, CONTAINER_WIDTH.toFloat(), 0f, 0) + private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) + private val UP_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) + + @BeforeClass + @JvmStatic + fun beforeClass() { + // Glanceable hub requires Compose, no point running any of these tests if compose isn't + // enabled. + assumeTrue(ComposeFacade.isComposeAvailable()) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 6ff79660efec..9d997dae6836 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.content.Context import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper @@ -45,8 +46,6 @@ import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake -import com.android.systemui.communal.data.repository.FakeCommunalRepository -import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.compose.ComposeFacade.isComposeAvailable import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager @@ -98,6 +97,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.TestScope @@ -112,9 +112,8 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import java.util.Optional import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -150,8 +149,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var notificationInsetsController: NotificationInsetsController - @Mock private lateinit var mCommunalViewModel: CommunalViewModel - private lateinit var mCommunalRepository: FakeCommunalRepository + @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @@ -201,8 +199,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - mCommunalRepository = FakeCommunalRepository() - testScope = TestScope() fakeClock = FakeSystemClock() underTest = @@ -236,8 +232,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { mock(KeyguardMessageAreaController.Factory::class.java), keyguardTransitionInteractor, primaryBouncerToGoneTransitionViewModel, - mCommunalViewModel, - mCommunalRepository, + mGlanceableHubContainerController, notificationLaunchAnimationInteractor, featureFlagsClassic, fakeClock, @@ -466,6 +461,16 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { } @Test + fun handleDispatchTouchEvent_glanceableHubIntercepts_returnsTrue() { + whenever(mGlanceableHubContainerController.onTouchEvent(DOWN_EVENT)).thenReturn(true) + underTest.setStatusBarViewController(phoneStatusBarViewController) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) + + assertThat(returnVal).isTrue() + } + + @Test fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() { // down event should be intercepted by keyguardViewManager whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) @@ -546,7 +551,11 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { return } - mCommunalRepository.setIsCommunalEnabled(true) + whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(true) + + val mockCommunalView = mock(View::class.java) + whenever(mGlanceableHubContainerController.initView(any<Context>())) + .thenReturn(mockCommunalView) val mockCommunalPlaceholder = mock(View::class.java) val fakeViewIndex = 20 @@ -558,7 +567,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { // Communal view added as a child of the container at the proper index, the stub is removed. verify(view).removeView(mockCommunalPlaceholder) - verify(view).addView(any(), eq(fakeViewIndex)) + verify(view).addView(eq(mockCommunalView), eq(fakeViewIndex)) } @Test @@ -567,7 +576,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { return } - mCommunalRepository.setIsCommunalEnabled(false) + whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(false) val mockCommunalPlaceholder = mock(View::class.java) val fakeViewIndex = 20 diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 29b1366eb6c1..9750f60c7b19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -41,8 +41,6 @@ import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake -import com.android.systemui.communal.data.repository.FakeCommunalRepository -import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer @@ -142,8 +140,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { private lateinit var unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider> @Mock private lateinit var notificationInsetsController: NotificationInsetsController - @Mock private lateinit var mCommunalViewModel: CommunalViewModel - private lateinit var mCommunalRepository: FakeCommunalRepository + @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @@ -180,8 +177,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition) .thenReturn(emptyFlow()) - mCommunalRepository = FakeCommunalRepository() - val featureFlags = FakeFeatureFlags() featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true) featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false) @@ -221,8 +216,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { Mockito.mock(KeyguardMessageAreaController.Factory::class.java), keyguardTransitionInteractor, primaryBouncerToGoneTransitionViewModel, - mCommunalViewModel, - mCommunalRepository, + mGlanceableHubContainerController, NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()), featureFlags, FakeSystemClock(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorInversionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorInversionRepository.kt new file mode 100644 index 000000000000..9a3b9e2cc701 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorInversionRepository.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import android.os.UserHandle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FakeColorInversionRepository : ColorInversionRepository { + private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>() + + override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> { + return getFlow(userHandle.identifier) + } + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean { + getFlow(userHandle.identifier).value = isEnabled + return true + } + + /** initializes the flow if already not */ + private fun getFlow(userId: Int): MutableStateFlow<Boolean> { + return userMap.getOrPut(userId) { MutableStateFlow(false) } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/inversion/ColorInversionTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/inversion/ColorInversionTileKosmos.kt new file mode 100644 index 000000000000..fad34aab05e0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/inversion/ColorInversionTileKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.inversion + +import com.android.systemui.accessibility.qs.QSAccessibilityModule +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger + +val Kosmos.qsColorInversionTileConfig by + Kosmos.Fixture { QSAccessibilityModule.provideColorInversionTileConfig(qsEventLogger) } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index a4b28967e3b2..77a5e3db2aba 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -72,7 +72,6 @@ import android.content.pm.ServiceInfo; import android.content.pm.ShortcutServiceInternal; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; -import android.content.pm.UserPackage; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; @@ -560,11 +559,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId); } else if (provider.maskedBySuspendedPackage) { showBadge = mUserManager.hasBadge(appUserId); - final UserPackage suspendingPackage = mPackageManagerInternal.getSuspendingPackage( + final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage( appInfo.packageName, appUserId); // TODO(b/281839596): don't rely on platform always meaning suspended by admin. - if (suspendingPackage != null - && PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) { + if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent( appUserId, true); } else { diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index cac2efba1c89..136692eb90d5 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -289,11 +289,11 @@ public abstract class PackageManagerInternal { * * @param suspendedPackage The package that has been suspended. * @param userId The user for which to check. - * @return User id and package name of the package that suspended the given package. Returns - * {@code null} if the given package is not currently suspended and the platform package name - * - i.e. {@code "android"} - if the package was suspended by a device admin. + * @return Name of the package that suspended the given package. Returns {@code null} if the + * given package is not currently suspended and the platform package name - i.e. + * {@code "android"} - if the package was suspended by a device admin. */ - public abstract UserPackage getSuspendingPackage(String suspendedPackage, int userId); + public abstract String getSuspendingPackage(String suspendedPackage, int userId); /** * Suspend or unsuspend packages upon admin request. @@ -312,13 +312,13 @@ public abstract class PackageManagerInternal { * suspended application. * * @param suspendedPackage The package that has been suspended. - * @param suspendingPackage The package responsible for suspension. + * @param suspendingPackage * @param userId The user for which to check. * @return A {@link SuspendDialogInfo} object describing the dialog to be shown. */ @Nullable public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, - UserPackage suspendingPackage, int userId); + String suspendingPackage, int userId); /** * Gets any distraction flags set via @@ -1168,14 +1168,14 @@ public abstract class PackageManagerInternal { public abstract void clearBlockUninstallForUser(@UserIdInt int userId); /** - * Unsuspends all packages suspended by an admin for the user. + * Unsuspends all packages suspended by the given package for the user. */ - public abstract void unsuspendAdminSuspendedPackages(int userId); + public abstract void unsuspendForSuspendingPackage(String suspendingPackage, int userId); /** - * Returns {@code true} if an admin is suspending any packages for the user. + * Returns {@code true} if the package is suspending any packages for the user. */ - public abstract boolean isAdminSuspendingAnyPackages(int userId); + public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId); /** * Register to listen for loading progress of an installed package. diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index c436c7217d0f..fd17261bda41 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -136,6 +136,7 @@ public class Watchdog implements Dumpable { "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec "media.transcoding", // Media transcoding service "com.android.bluetooth", // Bluetooth service + "/apex/com.android.art/bin/artd", // ART daemon "/apex/com.android.os.statsd/bin/statsd", // Stats daemon }; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2ee39c577977..a80d2fd32593 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2085,7 +2085,6 @@ public class ActivityManagerService extends IActivityManager.Stub app.makeActive(mSystemThread.getApplicationThread(), mProcessStats); app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_SYSTEM); addPidLocked(app); - mOomAdjuster.onProcessBeginLocked(app); updateLruProcessLocked(app, false, null); updateOomAdjLocked(OOM_ADJ_REASON_SYSTEM_INIT); } @@ -4624,7 +4623,6 @@ public class ActivityManagerService extends IActivityManager.Stub EventLogTags.writeAmProcBound(app.userId, pid, app.processName); synchronized (mProcLock) { - mOomAdjuster.onProcessBeginLocked(app); mOomAdjuster.setAttachingProcessStatesLSP(app); clearProcessForegroundLocked(app); app.setDebugging(false); @@ -7104,7 +7102,6 @@ public class ActivityManagerService extends IActivityManager.Stub sdkSandboxClientAppPackage, new HostingRecord(HostingRecord.HOSTING_TYPE_ADDED_APPLICATION, customProcess != null ? customProcess : info.processName)); - mOomAdjuster.onProcessBeginLocked(app); updateLruProcessLocked(app, false, null); updateOomAdjLocked(app, OOM_ADJ_REASON_PROCESS_BEGIN); } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index b00dcd6ccf1f..3424578a78d2 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -41,7 +41,6 @@ import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; -import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ALLOWLIST; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BACKUP; @@ -3494,8 +3493,8 @@ public class OomAdjuster { int initialCapability = PROCESS_CAPABILITY_NONE; boolean initialCached = true; final ProcessStateRecord state = app.mState; - final int prevProcState = PROCESS_STATE_UNKNOWN; - final int prevAdj = UNKNOWN_ADJ; + final int prevProcState = state.getCurRawProcState(); + final int prevAdj = state.getCurRawAdj(); // If the process has been marked as foreground, it is starting as the top app (with // Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread. if (state.hasForegroundActivities()) { @@ -3782,11 +3781,6 @@ public class OomAdjuster { } @GuardedBy("mService") - void onProcessBeginLocked(@NonNull ProcessRecord app) { - // Empty, the OomAdjusterModernImpl will have an implementation. - } - - @GuardedBy("mService") void onProcessEndLocked(@NonNull ProcessRecord app) { // Empty, the OomAdjusterModernImpl will have an implementation. } diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index d372108e0a47..7cc7c517fa9c 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -331,9 +331,9 @@ public class OomAdjusterModernImpl extends OomAdjuster { if (mLastNode[prevSlot] == node) { mLastNode[prevSlot] = node.mPrev; } - node.unlink(); } - mProcessRecordNodes[newSlot].append(node); + // node will be firstly unlinked in the append. + append(node, newSlot); } void moveAllNodesTo(int fromSlot, int toSlot) { @@ -389,7 +389,11 @@ public class OomAdjusterModernImpl extends OomAdjuster { } void append(@NonNull ProcessRecord app, int targetSlot) { - final ProcessRecordNode node = app.mLinkedNodes[mType]; + append(app.mLinkedNodes[mType], targetSlot); + } + + void append(@NonNull ProcessRecordNode node, int targetSlot) { + node.unlink(); mProcessRecordNodes[targetSlot].append(node); } @@ -452,6 +456,9 @@ public class OomAdjusterModernImpl extends OomAdjuster { @VisibleForTesting void reset() { + if (HEAD.mNext != TAIL) { + HEAD.mNext.mPrev = TAIL.mPrev.mNext = null; + } HEAD.mNext = TAIL; TAIL.mPrev = HEAD; } @@ -558,20 +565,6 @@ public class OomAdjusterModernImpl extends OomAdjuster { @GuardedBy("mService") @Override - void onProcessBeginLocked(@NonNull ProcessRecord app) { - // Check one type should be good enough. - if (app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE] == null) { - for (int i = 0; i < app.mLinkedNodes.length; i++) { - app.mLinkedNodes[i] = new ProcessRecordNode(app); - } - } - if (!app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE].isLinked()) { - linkProcessRecordToList(app); - } - } - - @GuardedBy("mService") - @Override void onProcessEndLocked(@NonNull ProcessRecord app) { if (app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE] != null && app.mLinkedNodes[ProcessRecordNode.NODE_TYPE_PROC_STATE].isLinked()) { diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index 40b1de623336..1412259abf89 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -299,6 +299,11 @@ class ProcessErrorStateRecord { SparseBooleanArray lastPids = new SparseBooleanArray(20); ActivityManagerService.VolatileDropboxEntryStates volatileDropboxEntriyStates = null; + if (mApp.isDebugging()) { + Slog.i(TAG, "Skipping debugged app ANR: " + this + " " + annotation); + return; + } + mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> { latencyTracker.waitingOnAMSLockStarted(); synchronized (mService) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 3156e9da0ae9..e57206ef9bc4 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2153,6 +2153,7 @@ public final class ProcessList { mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false, false, true, false, false, false, app.userId, "start failure"); + app.doEarlyCleanupIfNecessaryLocked(); } } }; @@ -2782,6 +2783,7 @@ public final class ProcessList { } noteAppKill(app, ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_INVALID_START, reason); + app.doEarlyCleanupIfNecessaryLocked(); return false; } mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index b4cd6a31291e..e5c4a66562c3 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -621,6 +621,34 @@ class ProcessRecord implements WindowProcessListener { mWindowProcessController = new WindowProcessController( mService.mActivityTaskManager, info, processName, uid, userId, this, this); mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode)); + updateProcessRecordNodes(this); + } + + /** + * Helper function to let test cases update the pointers. + */ + @VisibleForTesting + static void updateProcessRecordNodes(@NonNull ProcessRecord app) { + if (app.mService.mConstants.ENABLE_NEW_OOMADJ) { + for (int i = 0; i < app.mLinkedNodes.length; i++) { + app.mLinkedNodes[i] = new ProcessRecordNode(app); + } + } + } + + /** + * Perform cleanups if the process record is going to be discarded in an early + * stage of the process lifecycle, specifically when the process has not even + * attached itself to the system_server. + */ + @GuardedBy("mService") + void doEarlyCleanupIfNecessaryLocked() { + if (getThread() == null) { + // It's not even attached, make sure we unlink its process nodes. + mService.mOomAdjuster.onProcessEndLocked(this); + } else { + // Let the binder died callback handle the cleanup. + } } void resetCrashingOnRestart() { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 290bb7e92c69..0c782318fe89 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -4505,13 +4505,13 @@ public class AudioService extends IAudioService.Stub private void dumpFlags(PrintWriter pw) { pw.println("\nFun with Flags: "); - pw.println("\tandroid.media.audio.Flags.autoPublicVolumeApiHardening:" + pw.println("\tandroid.media.audio.autoPublicVolumeApiHardening:" + autoPublicVolumeApiHardening()); - pw.println("\tandroid.media.audio.Flags.focusFreezeTestApi:" + pw.println("\tandroid.media.audio.focusFreezeTestApi:" + focusFreezeTestApi()); - pw.println("\tcom.android.media.audio.Flags.bluetoothMacAddressAnonymization:" + pw.println("\tcom.android.media.audio.bluetoothMacAddressAnonymization:" + bluetoothMacAddressAnonymization()); - pw.println("\tcom.android.media.audio.Flags.disablePrescaleAbsoluteVolume:" + pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:" + disablePrescaleAbsoluteVolume()); } diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index b5846b555747..a40dd7919402 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -252,9 +252,6 @@ public final class CompatChange extends CompatibilityChangeInfo { // If the change is gated by a platform version newer than the one currently installed // on the device, disregard the app's target sdk version. int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk()); - if (compareSdk != app.targetSdkVersion) { - compareSdk = app.targetSdkVersion; - } return compareSdk >= getEnableSinceTargetSdk(); } return true; diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index b54289321e89..2314bb772494 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -16,6 +16,8 @@ package com.android.server.display; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; @@ -40,6 +42,7 @@ import android.os.Trace; import android.util.EventLog; import android.util.MathUtils; import android.util.Slog; +import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; @@ -49,6 +52,8 @@ import com.android.server.EventLogTags; import com.android.server.display.brightness.BrightnessEvent; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Manages the associated display brightness when in auto-brightness mode. This is also @@ -64,6 +69,16 @@ public class AutomaticBrightnessController { public static final int AUTO_BRIGHTNESS_DISABLED = 2; public static final int AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE = 3; + @IntDef(prefix = { "AUTO_BRIGHTNESS_MODE_" }, value = { + AUTO_BRIGHTNESS_MODE_DEFAULT, + AUTO_BRIGHTNESS_MODE_IDLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AutomaticBrightnessMode{} + + public static final int AUTO_BRIGHTNESS_MODE_DEFAULT = 0; + public static final int AUTO_BRIGHTNESS_MODE_IDLE = 1; + // How long the current sensor reading is assumed to be valid beyond the current time. // This provides a bit of prediction, as well as ensures that the weight for the last sample is // non-zero, which in turn ensures that the total weight is non-zero. @@ -91,10 +106,11 @@ public class AutomaticBrightnessController { private final Sensor mLightSensor; // The mapper to translate ambient lux to screen brightness in the range [0, 1.0]. - @Nullable + @NonNull private BrightnessMappingStrategy mCurrentBrightnessMapper; - private final BrightnessMappingStrategy mInteractiveModeBrightnessMapper; - private final BrightnessMappingStrategy mIdleModeBrightnessMapper; + + // A map of Brightness Mapping Strategies indexed by AutomaticBrightnessMode + private final SparseArray<BrightnessMappingStrategy> mBrightnessMappingStrategyMap; // The minimum and maximum screen brightnesses. private final float mScreenBrightnessRangeMinimum; @@ -251,7 +267,7 @@ public class AutomaticBrightnessController { AutomaticBrightnessController(Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, - BrightnessMappingStrategy interactiveModeBrightnessMapper, + SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, @@ -261,26 +277,25 @@ public class AutomaticBrightnessController { HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, BrightnessRangeController brightnessModeController, - BrightnessThrottler brightnessThrottler, - BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userBrightness) { + BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userNits) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, - interactiveModeBrightnessMapper, - lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, - lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, - darkeningLightDebounceConfig, brighteningLightDebounceConfigIdle, - darkeningLightDebounceConfigIdle, resetAmbientLuxAfterWarmUpConfig, - ambientBrightnessThresholds, screenBrightnessThresholds, - ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context, - brightnessModeController, brightnessThrottler, idleModeBrightnessMapper, - ambientLightHorizonShort, ambientLightHorizonLong, userLux, userBrightness + brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax, + dozeScaleFactor, lightSensorRate, initialLightSensorRate, + brighteningLightDebounceConfig, darkeningLightDebounceConfig, + brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle, + resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, + screenBrightnessThresholds, ambientBrightnessThresholdsIdle, + screenBrightnessThresholdsIdle, context, brightnessModeController, + brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, + userNits ); } @VisibleForTesting AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, - BrightnessMappingStrategy interactiveModeBrightnessMapper, + SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, @@ -290,15 +305,14 @@ public class AutomaticBrightnessController { HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, BrightnessRangeController brightnessModeController, - BrightnessThrottler brightnessThrottler, - BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userBrightness) { + BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userNits) { mInjector = injector; mClock = injector.createClock(); mContext = context; mCallbacks = callbacks; mSensorManager = sensorManager; - mCurrentBrightnessMapper = interactiveModeBrightnessMapper; + mCurrentBrightnessMapper = brightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT); mScreenBrightnessRangeMinimum = brightnessMin; mScreenBrightnessRangeMaximum = brightnessMax; mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime; @@ -337,13 +351,12 @@ public class AutomaticBrightnessController { mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mBrightnessRangeController = brightnessModeController; mBrightnessThrottler = brightnessThrottler; - mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper; - mIdleModeBrightnessMapper = idleModeBrightnessMapper; - // Initialize to active (normal) screen brightness mode - switchToInteractiveScreenBrightnessMode(); + mBrightnessMappingStrategyMap = brightnessMappingStrategyMap; // Use the given short-term model - setScreenBrightnessByUser(userLux, userBrightness); + if (userNits != BrightnessMappingStrategy.INVALID_NITS) { + setScreenBrightnessByUser(userLux, getBrightnessFromNits(userNits)); + } } /** @@ -358,11 +371,8 @@ public class AutomaticBrightnessController { if (mLoggingEnabled == loggingEnabled) { return false; } - if (mInteractiveModeBrightnessMapper != null) { - mInteractiveModeBrightnessMapper.setLoggingEnabled(loggingEnabled); - } - if (mIdleModeBrightnessMapper != null) { - mIdleModeBrightnessMapper.setLoggingEnabled(loggingEnabled); + for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) { + mBrightnessMappingStrategyMap.valueAt(i).setLoggingEnabled(loggingEnabled); } mLoggingEnabled = loggingEnabled; return true; @@ -389,8 +399,7 @@ public class AutomaticBrightnessController { | (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0) | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE ? BrightnessEvent.FLAG_DOZE_SCALE : 0) - | (mCurrentBrightnessMapper.isForIdleMode() - ? BrightnessEvent.FLAG_IDLE_CURVE : 0)); + | (isInIdleMode() ? BrightnessEvent.FLAG_IDLE_CURVE : 0)); } if (!mAmbientLuxValid) { @@ -464,15 +473,13 @@ public class AutomaticBrightnessController { // Used internally to establish whether we have deviated from the default config. public boolean isDefaultConfig() { - if (isInIdleMode()) { - return false; - } - return mInteractiveModeBrightnessMapper.isDefaultConfig(); + return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_DEFAULT + && mCurrentBrightnessMapper.isDefaultConfig(); } // Called from APIs to get the configuration. public BrightnessConfiguration getDefaultConfig() { - return mInteractiveModeBrightnessMapper.getDefaultConfig(); + return mBrightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT).getDefaultConfig(); } /** @@ -527,8 +534,7 @@ public class AutomaticBrightnessController { } private boolean setScreenBrightnessByUser(float lux, float brightness) { - if (lux == BrightnessMappingStrategy.NO_USER_LUX - || brightness == BrightnessMappingStrategy.NO_USER_BRIGHTNESS) { + if (lux == BrightnessMappingStrategy.INVALID_LUX || Float.isNaN(brightness)) { return false; } mCurrentBrightnessMapper.addUserDataPoint(lux, brightness); @@ -543,7 +549,8 @@ public class AutomaticBrightnessController { public boolean setBrightnessConfiguration(BrightnessConfiguration configuration, boolean shouldResetShortTermModel) { - if (mInteractiveModeBrightnessMapper.setBrightnessConfiguration(configuration)) { + if (mBrightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT) + .setBrightnessConfiguration(configuration)) { if (!isInIdleMode() && shouldResetShortTermModel) { resetShortTermModel(); } @@ -553,7 +560,7 @@ public class AutomaticBrightnessController { } public boolean isInIdleMode() { - return mCurrentBrightnessMapper.isForIdleMode(); + return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE; } public void dump(PrintWriter pw) { @@ -595,12 +602,6 @@ public class AutomaticBrightnessController { pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer); pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy)); - pw.println(" mShortTermModelTimeout(active)=" - + mInteractiveModeBrightnessMapper.getShortTermModelTimeout()); - if (mIdleModeBrightnessMapper != null) { - pw.println(" mShortTermModelTimeout(idle)=" - + mIdleModeBrightnessMapper.getShortTermModelTimeout()); - } pw.println(" mShortTermModel="); mShortTermModel.dump(pw); pw.println(" mPausedShortTermModel="); @@ -615,15 +616,14 @@ public class AutomaticBrightnessController { pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName); pw.println(" mForegroundAppCategory=" + mForegroundAppCategory); pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory); - pw.println(" Idle mode active=" + mCurrentBrightnessMapper.isForIdleMode()); + pw.println(" Current mode=" + mCurrentBrightnessMapper.getMode()); pw.println(); - pw.println(" mInteractiveMapper="); - mInteractiveModeBrightnessMapper.dump(pw, - mBrightnessRangeController.getNormalBrightnessMax()); - if (mIdleModeBrightnessMapper != null) { - pw.println(" mIdleMapper="); - mIdleModeBrightnessMapper.dump(pw, mBrightnessRangeController.getNormalBrightnessMax()); + for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) { + pw.println(" Mapper for mode " + modeToString(mBrightnessMappingStrategyMap.keyAt(i)) + + "="); + mBrightnessMappingStrategyMap.valueAt(i).dump(pw, + mBrightnessRangeController.getNormalBrightnessMax()); } pw.println(); @@ -1117,68 +1117,58 @@ public class AutomaticBrightnessController { updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */); } - void switchToIdleMode() { - if (mIdleModeBrightnessMapper == null) { - return; - } - if (mCurrentBrightnessMapper.isForIdleMode()) { - return; - } - Slog.i(TAG, "Switching to Idle Screen Brightness Mode"); + private void switchModeAndShortTermModels(@AutomaticBrightnessMode int mode) { // Stash short term model ShortTermModel tempShortTermModel = new ShortTermModel(); tempShortTermModel.set(mCurrentBrightnessMapper.getUserLux(), mCurrentBrightnessMapper.getUserBrightness(), /* valid= */ true); - + mHandler.removeMessages(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL); // Send delayed timeout mHandler.sendEmptyMessageAtTime(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL, mClock.uptimeMillis() + mCurrentBrightnessMapper.getShortTermModelTimeout()); - Slog.i(TAG, "mPreviousShortTermModel" + mPausedShortTermModel); + Slog.i(TAG, "mPreviousShortTermModel: " + mPausedShortTermModel); // new brightness mapper - mCurrentBrightnessMapper = mIdleModeBrightnessMapper; + mCurrentBrightnessMapper = mBrightnessMappingStrategyMap.get(mode); // if previous stm has been invalidated, and lux has drastically changed, just use // the new, reset stm. // if previous stm is still valid then revalidate it - if (mPausedShortTermModel != null && !mPausedShortTermModel.maybeReset(mAmbientLux)) { - setScreenBrightnessByUser(mPausedShortTermModel.mAnchor, - mPausedShortTermModel.mBrightness); + if (mPausedShortTermModel != null) { + if (!mPausedShortTermModel.maybeReset(mAmbientLux)) { + setScreenBrightnessByUser(mPausedShortTermModel.mAnchor, + mPausedShortTermModel.mBrightness); + } + mPausedShortTermModel.copyFrom(tempShortTermModel); } - mPausedShortTermModel.copyFrom(tempShortTermModel); update(); } - void switchToInteractiveScreenBrightnessMode() { - if (!mCurrentBrightnessMapper.isForIdleMode()) { + void switchMode(@AutomaticBrightnessMode int mode) { + if (!mBrightnessMappingStrategyMap.contains(mode)) { return; } - Slog.i(TAG, "Switching to Interactive Screen Brightness Mode"); - ShortTermModel tempShortTermModel = new ShortTermModel(); - tempShortTermModel.set(mCurrentBrightnessMapper.getUserLux(), - mCurrentBrightnessMapper.getUserBrightness(), /* valid= */ true); - mHandler.removeMessages(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL); - // Send delayed timeout - mHandler.sendEmptyMessageAtTime(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL, - mClock.uptimeMillis() - + mCurrentBrightnessMapper.getShortTermModelTimeout()); - Slog.i(TAG, "mPreviousShortTermModel" + mPausedShortTermModel.toString()); - - // restore interactive mapper. - mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper; - - // if previous stm has been invalidated, and lux has drastically changed, just use - // the new, reset stm. - // if previous stm is still valid then revalidate it - if (!mPausedShortTermModel.maybeReset(mAmbientLux)) { - setScreenBrightnessByUser(mPausedShortTermModel.mAnchor, - mPausedShortTermModel.mBrightness); + if (mCurrentBrightnessMapper.getMode() == mode) { + return; + } + Slog.i(TAG, "Switching to mode " + mode); + if (mode == AUTO_BRIGHTNESS_MODE_IDLE + || mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE) { + switchModeAndShortTermModels(mode); + } else { + resetShortTermModel(); + mCurrentBrightnessMapper = mBrightnessMappingStrategyMap.get(mode); } - mPausedShortTermModel.copyFrom(tempShortTermModel); + } - update(); + float getUserLux() { + return mCurrentBrightnessMapper.getUserLux(); + } + + float getUserNits() { + return convertToNits(mCurrentBrightnessMapper.getUserBrightness()); } /** @@ -1187,14 +1177,11 @@ public class AutomaticBrightnessController { * passing the brightness value to follower displays. * * @param brightness The float scale value - * @return The nit value or -1f if no conversion is possible. + * @return The nit value or {@link BrightnessMappingStrategy.INVALID_NITS} if no conversion is + * possible. */ public float convertToNits(float brightness) { - if (mCurrentBrightnessMapper != null) { - return mCurrentBrightnessMapper.convertToNits(brightness); - } else { - return -1.0f; - } + return mCurrentBrightnessMapper.convertToNits(brightness); } /** @@ -1203,14 +1190,11 @@ public class AutomaticBrightnessController { * {@link com.android.server.display.BrightnessTracker}. * * @param brightness The float scale value - * @return The nit value or -1f if no conversion is possible. + * @return The nit value or {@link BrightnessMappingStrategy.INVALID_NITS} if no conversion is + * possible. */ public float convertToAdjustedNits(float brightness) { - if (mCurrentBrightnessMapper != null) { - return mCurrentBrightnessMapper.convertToAdjustedNits(brightness); - } else { - return -1.0f; - } + return mCurrentBrightnessMapper.convertToAdjustedNits(brightness); } /** @@ -1221,12 +1205,8 @@ public class AutomaticBrightnessController { * @return The float scale value or {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if no * conversion is possible. */ - public float convertToFloatScale(float nits) { - if (mCurrentBrightnessMapper != null) { - return mCurrentBrightnessMapper.convertToFloatScale(nits); - } else { - return PowerManager.BRIGHTNESS_INVALID_FLOAT; - } + public float getBrightnessFromNits(float nits) { + return mCurrentBrightnessMapper.getBrightnessFromNits(nits); } public void recalculateSplines(boolean applyAdjustment, float[] adjustment) { @@ -1244,19 +1224,27 @@ public class AutomaticBrightnessController { } } + private String modeToString(@AutomaticBrightnessMode int mode) { + return switch (mode) { + case AUTO_BRIGHTNESS_MODE_DEFAULT -> "default"; + case AUTO_BRIGHTNESS_MODE_IDLE -> "idle"; + default -> Integer.toString(mode); + }; + } + private class ShortTermModel { // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the // user's adjustment) immediately, but wait for a drastic enough change in the ambient // light. // The anchor determines what were the light levels when the user has set their preference, // and we use a relative threshold to determine when to revert to the OEM curve. - private float mAnchor = BrightnessMappingStrategy.NO_USER_LUX; - private float mBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS; + private float mAnchor = BrightnessMappingStrategy.INVALID_LUX; + private float mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; private boolean mIsValid = false; private void reset() { - mAnchor = BrightnessMappingStrategy.NO_USER_LUX; - mBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS; + mAnchor = BrightnessMappingStrategy.INVALID_LUX; + mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; mIsValid = false; } @@ -1279,10 +1267,8 @@ public class AutomaticBrightnessController { private boolean maybeReset(float currentLux) { // If the short term model was invalidated and the change is drastic enough, reset it. // Otherwise, we revalidate it. - if (!mIsValid && mAnchor != -1) { - if (mCurrentBrightnessMapper != null - && mCurrentBrightnessMapper.shouldResetShortTermModel( - currentLux, mAnchor)) { + if (!mIsValid && mAnchor != BrightnessMappingStrategy.INVALID_LUX) { + if (mCurrentBrightnessMapper.shouldResetShortTermModel(currentLux, mAnchor)) { resetShortTermModel(); } else { mIsValid = true; @@ -1304,7 +1290,7 @@ public class AutomaticBrightnessController { } public String toString() { - return " mAnchor: " + mAnchor + return "mAnchor: " + mAnchor + "\n mBrightness: " + mBrightness + "\n mIsValid: " + mIsValid; } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index d848f4b6cce5..acd253b38b3d 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -18,6 +18,9 @@ package com.android.server.display; import static android.text.TextUtils.formatSimple; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; + import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.content.res.Resources; @@ -57,8 +60,8 @@ import java.util.Objects; public abstract class BrightnessMappingStrategy { private static final String TAG = "BrightnessMappingStrategy"; - public static final float NO_USER_LUX = -1; - public static final float NO_USER_BRIGHTNESS = -1; + public static final float INVALID_LUX = -1; + public static final float INVALID_NITS = -1; private static final float LUX_GRAD_SMOOTHING = 0.25f; private static final float MAX_GRAD = 1.0f; @@ -74,75 +77,48 @@ public abstract class BrightnessMappingStrategy { private static final Plog PLOG = Plog.createSystemPlog(TAG); /** - * Creates a BrightnessMappingStrategy for active (normal) mode. - * @param resources - * @param displayDeviceConfig - * @param displayWhiteBalanceController - * @return the BrightnessMappingStrategy - */ - @Nullable - public static BrightnessMappingStrategy create(Resources resources, - DisplayDeviceConfig displayDeviceConfig, - DisplayWhiteBalanceController displayWhiteBalanceController) { - return create(resources, displayDeviceConfig, /* isForIdleMode= */ false, - displayWhiteBalanceController); - } - - /** - * Creates a BrightnessMappingStrategy for idle screen brightness mode. - * @param resources - * @param displayDeviceConfig - * @param displayWhiteBalanceController - * @return the BrightnessMappingStrategy - */ - @Nullable - public static BrightnessMappingStrategy createForIdleMode(Resources resources, - DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController - displayWhiteBalanceController) { - return create(resources, displayDeviceConfig, /* isForIdleMode= */ true, - displayWhiteBalanceController); - } - - /** - * Creates a BrightnessMapping strategy for either active or idle screen brightness mode. - * We do not create a simple mapping strategy for idle mode. + * Creates a BrightnessMapping strategy. We do not create a simple mapping strategy for idle + * mode. * * @param resources * @param displayDeviceConfig - * @param isForIdleMode determines whether the configurations loaded are for idle screen - * brightness mode or active screen brightness mode. + * @param mode The auto-brightness mode. Different modes use different brightness curves * @param displayWhiteBalanceController * @return the BrightnessMappingStrategy */ @Nullable - private static BrightnessMappingStrategy create(Resources resources, - DisplayDeviceConfig displayDeviceConfig, boolean isForIdleMode, + static BrightnessMappingStrategy create(Resources resources, + DisplayDeviceConfig displayDeviceConfig, + @AutomaticBrightnessController.AutomaticBrightnessMode int mode, DisplayWhiteBalanceController displayWhiteBalanceController) { // Display independent, mode dependent values - float[] brightnessLevelsNits; + float[] brightnessLevelsNits = null; float[] brightnessLevels = null; - float[] luxLevels; - if (isForIdleMode) { - brightnessLevelsNits = getFloatArray(resources.obtainTypedArray( - com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle)); - luxLevels = getLuxLevels(resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLevelsIdle)); - } else { - brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(); - luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(); - - brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels(); - if (brightnessLevels == null || brightnessLevels.length == 0) { - // Load the old configuration in the range [0, 255]. The values need to be - // normalized to the range [0, 1]. - int[] brightnessLevelsInt = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); - brightnessLevels = new float[brightnessLevelsInt.length]; - for (int i = 0; i < brightnessLevels.length; i++) { - brightnessLevels[i] = normalizeAbsoluteBrightness(brightnessLevelsInt[i]); + float[] luxLevels = null; + switch (mode) { + case AUTO_BRIGHTNESS_MODE_DEFAULT: + brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(); + luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(); + + brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels(); + if (brightnessLevels == null || brightnessLevels.length == 0) { + // Load the old configuration in the range [0, 255]. The values need to be + // normalized to the range [0, 1]. + int[] brightnessLevelsInt = resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); + brightnessLevels = new float[brightnessLevelsInt.length]; + for (int i = 0; i < brightnessLevels.length; i++) { + brightnessLevels[i] = normalizeAbsoluteBrightness(brightnessLevelsInt[i]); + } } - } + break; + case AUTO_BRIGHTNESS_MODE_IDLE: + brightnessLevelsNits = getFloatArray(resources.obtainTypedArray( + com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle)); + luxLevels = getLuxLevels(resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLevelsIdle)); + break; } // Display independent, mode independent values @@ -158,17 +134,16 @@ public abstract class BrightnessMappingStrategy { if (isValidMapping(nitsRange, brightnessRange) && isValidMapping(luxLevels, brightnessLevelsNits)) { - BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( luxLevels, brightnessLevelsNits); builder.setShortTermModelTimeoutMillis(shortTermModelTimeout); builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange, - autoBrightnessAdjustmentMaxGamma, isForIdleMode, displayWhiteBalanceController); + autoBrightnessAdjustmentMaxGamma, mode, displayWhiteBalanceController); } else if (isValidMapping(luxLevels, brightnessLevels)) { return new SimpleMappingStrategy(luxLevels, brightnessLevels, - autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout); + autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout, mode); } else { return null; } @@ -334,7 +309,7 @@ public abstract class BrightnessMappingStrategy { /** * Converts the provided brightness value to nits if possible. * - * Returns -1.0f if there's no available mapping for the brightness to nits. + * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits. */ public abstract float convertToNits(float brightness); @@ -342,7 +317,7 @@ public abstract class BrightnessMappingStrategy { * Converts the provided brightness value to nits if possible. Adjustments, such as RBC are * applied. * - * Returns -1.0f if there's no available mapping for the brightness to nits. + * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits. */ public abstract float convertToAdjustedNits(float brightness); @@ -352,7 +327,7 @@ public abstract class BrightnessMappingStrategy { * Returns {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if there's no available mapping for * the nits to float scale. */ - public abstract float convertToFloatScale(float nits); + public abstract float getBrightnessFromNits(float nits); /** * Adds a user interaction data point to the brightness mapping. @@ -407,17 +382,18 @@ public abstract class BrightnessMappingStrategy { */ public abstract void dump(PrintWriter pw, float hbmTransition); - /** - * We can designate a mapping strategy to be used for idle screen brightness mode. - * @return whether this mapping strategy is to be used for idle screen brightness mode. - */ - public abstract boolean isForIdleMode(); - abstract float getUserLux(); abstract float getUserBrightness(); /** + * @return The auto-brightness mode of this mapping strategy. Different modes use different + * brightness curves. + */ + @AutomaticBrightnessController.AutomaticBrightnessMode + abstract int getMode(); + + /** * Check if the short term model should be reset given the anchor lux the last * brightness change was made at and the current ambient lux. */ @@ -596,7 +572,7 @@ public abstract class BrightnessMappingStrategy { if (mLoggingEnabled) { PLOG.logCurve("gamma adjusted curve", newLux, newBrightness); } - if (userLux != -1) { + if (userLux != INVALID_LUX) { Pair<float[], float[]> curve = insertControlPoint(newLux, newBrightness, userLux, userBrightness); newLux = curve.first; @@ -624,6 +600,9 @@ public abstract class BrightnessMappingStrategy { // Brightness control points normalized to [0, 1] private final float[] mBrightness; + @AutomaticBrightnessController.AutomaticBrightnessMode + private final int mMode; + private Spline mSpline; private float mMaxGamma; private float mAutoBrightnessAdjustment; @@ -632,7 +611,7 @@ public abstract class BrightnessMappingStrategy { private long mShortTermModelTimeout; private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma, - long timeout) { + long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, "Lux and brightness arrays must not be empty!"); Preconditions.checkArgument(lux.length == brightness.length, @@ -651,13 +630,14 @@ public abstract class BrightnessMappingStrategy { mMaxGamma = maxGamma; mAutoBrightnessAdjustment = 0; - mUserLux = NO_USER_LUX; - mUserBrightness = NO_USER_BRIGHTNESS; + mUserLux = INVALID_LUX; + mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; if (mLoggingEnabled) { PLOG.start("simple mapping strategy"); } computeSpline(); mShortTermModelTimeout = timeout; + mMode = mode; } @Override @@ -704,16 +684,16 @@ public abstract class BrightnessMappingStrategy { @Override public float convertToNits(float brightness) { - return -1.0f; + return INVALID_NITS; } @Override public float convertToAdjustedNits(float brightness) { - return -1.0f; + return INVALID_NITS; } @Override - public float convertToFloatScale(float nits) { + public float getBrightnessFromNits(float nits) { return PowerManager.BRIGHTNESS_INVALID_FLOAT; } @@ -741,22 +721,22 @@ public abstract class BrightnessMappingStrategy { @Override public void clearUserDataPoints() { - if (mUserLux != -1) { + if (mUserLux != INVALID_LUX) { if (mLoggingEnabled) { Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); PLOG.start("clear user data points") .logPoint("user data point", mUserLux, mUserBrightness); } mAutoBrightnessAdjustment = 0; - mUserLux = -1; - mUserBrightness = -1; + mUserLux = INVALID_LUX; + mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; computeSpline(); } } @Override public boolean hasUserDataPoints() { - return mUserLux != -1; + return mUserLux != INVALID_LUX; } @Override @@ -782,11 +762,12 @@ public abstract class BrightnessMappingStrategy { pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mUserLux=" + mUserLux); pw.println(" mUserBrightness=" + mUserBrightness); + pw.println(" mShortTermModelTimeout=" + mShortTermModelTimeout); } @Override - public boolean isForIdleMode() { - return false; + int getMode() { + return mMode; } @Override @@ -854,9 +835,11 @@ public abstract class BrightnessMappingStrategy { private float mAutoBrightnessAdjustment; private float mUserLux; private float mUserBrightness; - private final boolean mIsForIdleMode; private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; + @AutomaticBrightnessController.AutomaticBrightnessMode + private final int mMode; + // Previous short-term models and the times that they were computed stored for debugging // purposes private List<Spline> mPreviousBrightnessSplines = new ArrayList<>(); @@ -865,7 +848,8 @@ public abstract class BrightnessMappingStrategy { private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, - float[] brightness, float maxGamma, boolean isForIdleMode, + float[] brightness, float maxGamma, + @AutomaticBrightnessController.AutomaticBrightnessMode int mode, DisplayWhiteBalanceController displayWhiteBalanceController) { Preconditions.checkArgument(nits.length != 0 && brightness.length != 0, @@ -878,11 +862,11 @@ public abstract class BrightnessMappingStrategy { Preconditions.checkArrayElementsInRange(brightness, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness"); - mIsForIdleMode = isForIdleMode; + mMode = mode; mMaxGamma = maxGamma; mAutoBrightnessAdjustment = 0; - mUserLux = NO_USER_LUX; - mUserBrightness = NO_USER_BRIGHTNESS; + mUserLux = INVALID_LUX; + mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; mDisplayWhiteBalanceController = displayWhiteBalanceController; mNits = nits; @@ -982,7 +966,7 @@ public abstract class BrightnessMappingStrategy { } @Override - public float convertToFloatScale(float nits) { + public float getBrightnessFromNits(float nits) { return mNitsToBrightnessSpline.interpolate(nits); } @@ -1024,15 +1008,15 @@ public abstract class BrightnessMappingStrategy { .logPoint("user data point", mUserLux, mUserBrightness); } mAutoBrightnessAdjustment = 0; - mUserLux = -1; - mUserBrightness = -1; + mUserLux = INVALID_LUX; + mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; computeSpline(); } } @Override public boolean hasUserDataPoints() { - return mUserLux != -1; + return mUserLux != INVALID_LUX; } @Override @@ -1072,6 +1056,7 @@ public abstract class BrightnessMappingStrategy { pw.println(" mUserBrightness=" + mUserBrightness); pw.println(" mDefaultConfig=" + mDefaultConfig); pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied); + pw.println(" shortTermModelTimeout=" + getShortTermModelTimeout()); pw.println(" Previous short-term models (oldest to newest): "); for (int i = 0; i < mPreviousBrightnessSplines.size(); i++) { @@ -1086,8 +1071,8 @@ public abstract class BrightnessMappingStrategy { } @Override - public boolean isForIdleMode() { - return mIsForIdleMode; + int getMode() { + return mMode; } @Override diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 2ab15e639d68..e38d08ff2a1a 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2880,7 +2880,9 @@ public final class DisplayManagerService extends SystemService { final DisplayPowerControllerInterface displayPowerController = mDisplayPowerControllers.get(displayId); if (displayPowerController != null) { - displayPowerController.setAutomaticScreenBrightnessMode(enabled); + displayPowerController.setAutomaticScreenBrightnessMode(enabled + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE + : AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT); } } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 2cca72e4c883..2685efecd6a1 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -16,6 +16,9 @@ package com.android.server.display; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.NonNull; @@ -53,7 +56,6 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.util.FloatProperty; -import android.util.Log; import android.util.MathUtils; import android.util.MutableFloat; import android.util.MutableInt; @@ -498,15 +500,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private Sensor mLightSensor; private Sensor mScreenOffBrightnessSensor; - // The mappers between ambient lux, display backlight values, and display brightness. - // We will switch between the idle mapper and active mapper in AutomaticBrightnessController. - // Mapper used for active (normal) screen brightness mode - @Nullable - private BrightnessMappingStrategy mInteractiveModeBrightnessMapper; - // Mapper used for idle screen brightness mode - @Nullable - private BrightnessMappingStrategy mIdleModeBrightnessMapper; - // The current brightness configuration. @Nullable private BrightnessConfiguration mBrightnessConfiguration; @@ -609,7 +602,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting, Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata, boolean bootCompleted, DisplayManagerFlags flags) { - + mFlags = flags; mInjector = injector != null ? injector : new Injector(); mClock = mInjector.getClock(); mLogicalDisplay = logicalDisplay; @@ -767,8 +760,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; mBootCompleted = bootCompleted; - - mFlags = flags; } private void applyReduceBrightColorsSplineAdjustment() { @@ -780,13 +771,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mAutomaticBrightnessController == null) { return; } - if ((!mAutomaticBrightnessController.isInIdleMode() - && mInteractiveModeBrightnessMapper == null) - || (mAutomaticBrightnessController.isInIdleMode() - && mIdleModeBrightnessMapper == null)) { - Log.w(mTag, "No brightness mapping available to recalculate splines for this mode"); - return; - } float[] adjustedNits = new float[mNitsRange.length]; for (int i = 0; i < mNitsRange.length; i++) { @@ -848,10 +832,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, boolean slowChange) { mBrightnessRangeController.onAmbientLuxChange(ambientLux); - if (nits < 0) { + if (nits == BrightnessMappingStrategy.INVALID_NITS) { mBrightnessToFollow = leadDisplayBrightness; } else { - float brightness = convertToFloatScale(nits); + float brightness = getBrightnessFromNits(nits); if (isValidBrightnessValue(brightness)) { mBrightnessToFollow = brightness; } else { @@ -876,7 +860,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call synchronized (mLock) { mDisplayBrightnessFollowers.remove(follower.getDisplayId()); mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } } @@ -886,7 +870,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } mDisplayBrightnessFollowers.clear(); @@ -1168,24 +1152,35 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return; } - float userLux = BrightnessMappingStrategy.NO_USER_LUX; - float userNits = -1; - if (mInteractiveModeBrightnessMapper != null) { - userLux = mInteractiveModeBrightnessMapper.getUserLux(); - float userBrightness = mInteractiveModeBrightnessMapper.getUserBrightness(); - userNits = mInteractiveModeBrightnessMapper.convertToNits(userBrightness); - } + SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>(); + + BrightnessMappingStrategy defaultModeBrightnessMapper = + mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig, + mDisplayWhiteBalanceController); + brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT, + defaultModeBrightnessMapper); final boolean isIdleScreenBrightnessEnabled = resources.getBoolean( R.bool.config_enableIdleScreenBrightnessMode); - mInteractiveModeBrightnessMapper = mInjector.getInteractiveModeBrightnessMapper(resources, - mDisplayDeviceConfig, mDisplayWhiteBalanceController); if (isIdleScreenBrightnessEnabled) { - mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources, - mDisplayDeviceConfig, mDisplayWhiteBalanceController); + BrightnessMappingStrategy idleModeBrightnessMapper = + BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig, + AUTO_BRIGHTNESS_MODE_IDLE, + mDisplayWhiteBalanceController); + if (idleModeBrightnessMapper != null) { + brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, + idleModeBrightnessMapper); + } + } + + float userLux = BrightnessMappingStrategy.INVALID_LUX; + float userNits = BrightnessMappingStrategy.INVALID_NITS; + if (mAutomaticBrightnessController != null) { + userLux = mAutomaticBrightnessController.getUserLux(); + userNits = mAutomaticBrightnessController.getUserNits(); } - if (mInteractiveModeBrightnessMapper != null) { + if (defaultModeBrightnessMapper != null) { final float dozeScaleFactor = resources.getFraction( com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); @@ -1297,25 +1292,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.stop(); } - float userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS; - if (userNits >= 0) { - userBrightness = mInteractiveModeBrightnessMapper.convertToFloatScale(userNits); - if (Float.isNaN(userBrightness)) { - userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS; - } - } mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController( this, handler.getLooper(), mSensorManager, mLightSensor, - mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, - lightSensorRate, initialLightSensorRate, brighteningLightDebounce, - darkeningLightDebounce, brighteningLightDebounceIdle, - darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp, - ambientBrightnessThresholds, screenBrightnessThresholds, - ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext, - mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper, - mDisplayDeviceConfig.getAmbientHorizonShort(), - mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userBrightness); + brightnessMappers, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, + initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, + brighteningLightDebounceIdle, darkeningLightDebounceIdle, + autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, + screenBrightnessThresholds, ambientBrightnessThresholdsIdle, + screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, + mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(), + mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits); mBrightnessEventRingBuffer = new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); @@ -1334,7 +1321,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mHandler, SystemClock::uptimeMillis, sensorValueToLux, - mInteractiveModeBrightnessMapper); + defaultModeBrightnessMapper); } } else { mUseSoftwareAutoBrightnessConfig = false; @@ -1377,13 +1364,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } @Override - public void setAutomaticScreenBrightnessMode(boolean isIdle) { + public void setAutomaticScreenBrightnessMode( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { + boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE; if (mAutomaticBrightnessController != null) { - if (isIdle) { - mAutomaticBrightnessController.switchToIdleMode(); - } else { - mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode(); - } + mAutomaticBrightnessController.switchMode(mode); setAnimatorRampSpeeds(isIdle); } @@ -2810,7 +2795,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call float brightnessNitsForDefaultDisplay = mBrightnessSetting.getBrightnessNitsForDefaultDisplay(); if (brightnessNitsForDefaultDisplay >= 0) { - float brightnessForDefaultDisplay = convertToFloatScale( + float brightnessForDefaultDisplay = getBrightnessFromNits( brightnessNitsForDefaultDisplay); if (isValidBrightnessValue(brightnessForDefaultDisplay)) { mBrightnessSetting.setBrightness(brightnessForDefaultDisplay); @@ -2944,23 +2929,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private float convertToNits(float brightness) { if (mAutomaticBrightnessController == null) { - return -1f; + return BrightnessMappingStrategy.INVALID_NITS; } return mAutomaticBrightnessController.convertToNits(brightness); } private float convertToAdjustedNits(float brightness) { if (mAutomaticBrightnessController == null) { - return -1f; + return BrightnessMappingStrategy.INVALID_NITS; } return mAutomaticBrightnessController.convertToAdjustedNits(brightness); } - private float convertToFloatScale(float nits) { + private float getBrightnessFromNits(float nits) { if (mAutomaticBrightnessController == null) { return PowerManager.BRIGHTNESS_INVALID_FLOAT; } - return mAutomaticBrightnessController.convertToFloatScale(nits); + return mAutomaticBrightnessController.getBrightnessFromNits(nits); } @GuardedBy("mLock") @@ -3635,7 +3620,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, - BrightnessMappingStrategy interactiveModeBrightnessMapper, + SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, @@ -3645,27 +3630,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, - BrightnessRangeController brightnessRangeController, - BrightnessThrottler brightnessThrottler, - BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userBrightness) { + BrightnessRangeController brightnessModeController, + BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userNits) { return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, - interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin, + brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, darkeningLightDebounceConfig, brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle, resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, screenBrightnessThresholds, ambientBrightnessThresholdsIdle, - screenBrightnessThresholdsIdle, context, brightnessRangeController, - brightnessThrottler, idleModeBrightnessMapper, ambientLightHorizonShort, - ambientLightHorizonLong, userLux, userBrightness); + screenBrightnessThresholdsIdle, context, brightnessModeController, + brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, + userNits); } - BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { return BrightnessMappingStrategy.create(resources, - displayDeviceConfig, displayWhiteBalanceController); + displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT, + displayWhiteBalanceController); } HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 810ac08e32c1..52c53f3d658e 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -16,6 +16,9 @@ package com.android.server.display; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.Nullable; @@ -52,7 +55,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.FloatProperty; import android.util.IndentingPrintWriter; -import android.util.Log; import android.util.MathUtils; import android.util.MutableFloat; import android.util.MutableInt; @@ -442,15 +444,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private Sensor mLightSensor; private Sensor mScreenOffBrightnessSensor; - // The mappers between ambient lux, display backlight values, and display brightness. - // We will switch between the idle mapper and active mapper in AutomaticBrightnessController. - // Mapper used for active (normal) screen brightness mode - @Nullable - private BrightnessMappingStrategy mInteractiveModeBrightnessMapper; - // Mapper used for idle screen brightness mode - @Nullable - private BrightnessMappingStrategy mIdleModeBrightnessMapper; - private boolean mIsRbcActive; // Animators. @@ -490,7 +483,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting, Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata, boolean bootCompleted, DisplayManagerFlags flags) { - + mFlags = flags; mInjector = injector != null ? injector : new Injector(); mClock = mInjector.getClock(); mLogicalDisplay = logicalDisplay; @@ -636,7 +629,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal R.bool.config_displayBrightnessBucketsInDoze); mBootCompleted = bootCompleted; - mFlags = flags; } private void applyReduceBrightColorsSplineAdjustment() { @@ -648,13 +640,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (mAutomaticBrightnessController == null) { return; } - if ((!mAutomaticBrightnessController.isInIdleMode() - && mInteractiveModeBrightnessMapper == null) - || (mAutomaticBrightnessController.isInIdleMode() - && mIdleModeBrightnessMapper == null)) { - Log.w(mTag, "No brightness mapping available to recalculate splines for this mode"); - return; - } float[] adjustedNits = new float[mNitsRange.length]; for (int i = 0; i < mNitsRange.length; i++) { @@ -987,24 +972,35 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal return; } - float userLux = BrightnessMappingStrategy.NO_USER_LUX; - float userNits = -1; - if (mInteractiveModeBrightnessMapper != null) { - userLux = mInteractiveModeBrightnessMapper.getUserLux(); - float userBrightness = mInteractiveModeBrightnessMapper.getUserBrightness(); - userNits = mInteractiveModeBrightnessMapper.convertToNits(userBrightness); - } + SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>(); + + BrightnessMappingStrategy defaultModeBrightnessMapper = + mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig, + mDisplayWhiteBalanceController); + brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT, + defaultModeBrightnessMapper); final boolean isIdleScreenBrightnessEnabled = resources.getBoolean( R.bool.config_enableIdleScreenBrightnessMode); - mInteractiveModeBrightnessMapper = mInjector.getInteractiveModeBrightnessMapper(resources, - mDisplayDeviceConfig, mDisplayWhiteBalanceController); if (isIdleScreenBrightnessEnabled) { - mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources, - mDisplayDeviceConfig, mDisplayWhiteBalanceController); + BrightnessMappingStrategy idleModeBrightnessMapper = + BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig, + AUTO_BRIGHTNESS_MODE_IDLE, + mDisplayWhiteBalanceController); + if (idleModeBrightnessMapper != null) { + brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, + idleModeBrightnessMapper); + } + } + + float userLux = BrightnessMappingStrategy.INVALID_LUX; + float userNits = BrightnessMappingStrategy.INVALID_NITS; + if (mAutomaticBrightnessController != null) { + userLux = mAutomaticBrightnessController.getUserLux(); + userNits = mAutomaticBrightnessController.getUserNits(); } - if (mInteractiveModeBrightnessMapper != null) { + if (defaultModeBrightnessMapper != null) { final float dozeScaleFactor = resources.getFraction( R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); @@ -1116,25 +1112,17 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.stop(); } - float userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS; - if (userNits >= 0) { - userBrightness = mInteractiveModeBrightnessMapper.convertToFloatScale(userNits); - if (Float.isNaN(userBrightness)) { - userBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS; - } - } mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController( this, handler.getLooper(), mSensorManager, mLightSensor, - mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, - lightSensorRate, initialLightSensorRate, brighteningLightDebounce, - darkeningLightDebounce, brighteningLightDebounceIdle, - darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp, - ambientBrightnessThresholds, screenBrightnessThresholds, - ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext, - mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper, - mDisplayDeviceConfig.getAmbientHorizonShort(), - mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userBrightness); + brightnessMappers, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, + initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, + brighteningLightDebounceIdle, darkeningLightDebounceIdle, + autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, + screenBrightnessThresholds, ambientBrightnessThresholdsIdle, + screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, + mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(), + mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits); mDisplayBrightnessController.setAutomaticBrightnessController( mAutomaticBrightnessController); @@ -1157,7 +1145,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mHandler, SystemClock::uptimeMillis, sensorValueToLux, - mInteractiveModeBrightnessMapper); + defaultModeBrightnessMapper); } } else { mUseSoftwareAutoBrightnessConfig = false; @@ -1200,13 +1188,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } @Override - public void setAutomaticScreenBrightnessMode(boolean isIdle) { + public void setAutomaticScreenBrightnessMode( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { + boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE; if (mAutomaticBrightnessController != null) { - if (isIdle) { - mAutomaticBrightnessController.switchToIdleMode(); - } else { - mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode(); - } + mAutomaticBrightnessController.switchMode(mode); setAnimatorRampSpeeds(isIdle); } Message msg = mHandler.obtainMessage(); @@ -2351,10 +2337,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, boolean slowChange) { mBrightnessRangeController.onAmbientLuxChange(ambientLux); - if (nits < 0) { + if (nits == BrightnessMappingStrategy.INVALID_NITS) { mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange); } else { - float brightness = mDisplayBrightnessController.convertToFloatScale(nits); + float brightness = mDisplayBrightnessController.getBrightnessFromNits(nits); if (BrightnessUtils.isValidBrightnessValue(brightness)) { mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange); } else { @@ -2421,7 +2407,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal synchronized (mLock) { mDisplayBrightnessFollowers.remove(follower.getDisplayId()); mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } } @@ -2431,7 +2417,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) { DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); mHandler.postAtTime(() -> follower.setBrightnessToFollow( - PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, + PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS, /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } mDisplayBrightnessFollowers.clear(); @@ -3012,7 +2998,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, - BrightnessMappingStrategy interactiveModeBrightnessMapper, + SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, @@ -3023,26 +3009,26 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, BrightnessRangeController brightnessModeController, - BrightnessThrottler brightnessThrottler, - BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, - int ambientLightHorizonLong, float userLux, float userBrightness) { + BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userNits) { return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, - interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin, + brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, darkeningLightDebounceConfig, brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle, resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context, brightnessModeController, - brightnessThrottler, idleModeBrightnessMapper, ambientLightHorizonShort, - ambientLightHorizonLong, userLux, userBrightness); + brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, + userNits); } - BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { return BrightnessMappingStrategy.create(resources, - displayDeviceConfig, displayWhiteBalanceController); + displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT, + displayWhiteBalanceController); } HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java index 72079a4c82fe..c27918430254 100644 --- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java +++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java @@ -69,9 +69,10 @@ public interface DisplayPowerControllerInterface { /** * Used to decide the associated AutomaticBrightnessController's BrightnessMode - * @param isIdle Flag which represents if the Idle BrightnessMode is to be set + * @param mode The auto-brightness mode */ - void setAutomaticScreenBrightnessMode(boolean isIdle); + void setAutomaticScreenBrightnessMode( + @AutomaticBrightnessController.AutomaticBrightnessMode int mode); /** * Used to enable/disable the logging of the WhileBalance associated entities diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index 617befbbd17d..3bb798689ea6 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -27,6 +27,7 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.AutomaticBrightnessController; +import com.android.server.display.BrightnessMappingStrategy; import com.android.server.display.BrightnessSetting; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; @@ -359,11 +360,12 @@ public final class DisplayBrightnessController { * passing the brightness value to follower displays. * * @param brightness The float scale value - * @return The nit value or -1f if no conversion is possible. + * @return The nit value or {@link BrightnessMappingStrategy.INVALID_NITS} if no conversion is + * possible. */ public float convertToNits(float brightness) { if (mAutomaticBrightnessController == null) { - return -1f; + return BrightnessMappingStrategy.INVALID_NITS; } return mAutomaticBrightnessController.convertToNits(brightness); } @@ -374,11 +376,12 @@ public final class DisplayBrightnessController { * {@link com.android.server.display.BrightnessTracker}. * * @param brightness The float scale value - * @return The nit value or -1f if no conversion is possible. + * @return The nit value or {@link BrightnessMappingStrategy.INVALID_NITS} if no conversion is + * possible. */ public float convertToAdjustedNits(float brightness) { if (mAutomaticBrightnessController == null) { - return -1f; + return BrightnessMappingStrategy.INVALID_NITS; } return mAutomaticBrightnessController.convertToAdjustedNits(brightness); } @@ -391,11 +394,11 @@ public final class DisplayBrightnessController { * @return The float scale value or {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if no * conversion is possible. */ - public float convertToFloatScale(float nits) { + public float getBrightnessFromNits(float nits) { if (mAutomaticBrightnessController == null) { return PowerManager.BRIGHTNESS_INVALID_FLOAT; } - return mAutomaticBrightnessController.convertToFloatScale(nits); + return mAutomaticBrightnessController.getBrightnessFromNits(nits); } /** @@ -497,7 +500,7 @@ public final class DisplayBrightnessController { float brightnessNitsForDefaultDisplay = mBrightnessSetting.getBrightnessNitsForDefaultDisplay(); if (brightnessNitsForDefaultDisplay >= 0) { - float brightnessForDefaultDisplay = convertToFloatScale( + float brightnessForDefaultDisplay = getBrightnessFromNits( brightnessNitsForDefaultDisplay); if (BrightnessUtils.isValidBrightnessValue(brightnessForDefaultDisplay)) { mBrightnessSetting.setBrightness(brightnessForDefaultDisplay); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index b3aa351d69d7..70993ca3e21b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -201,7 +201,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { if (WAKE_ON_HOTPLUG && connected) { mService.wakeUp(); } - if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) { + HdmiPortInfo portInfo = mService.getPortInfo(portId); + if (portInfo != null && portInfo.getType() == HdmiPortInfo.PORT_OUTPUT) { mCecMessageCache.flushAll(); if (!connected) { if (isSystemAudioActivated()) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 7e8a2cc6d835..29303aab6fa9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -92,7 +92,8 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly void onHotplug(int portId, boolean connected) { assertRunOnServiceThread(); - if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) { + HdmiPortInfo portInfo = mService.getPortInfo(portId); + if (portInfo != null && portInfo.getType() == HdmiPortInfo.PORT_OUTPUT) { mCecMessageCache.flushAll(); } // We'll not invalidate the active source on the hotplug event to pass CETC 11.2.2-2 ~ 3. diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index b3926fd1b5ba..92537064f766 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1783,8 +1783,9 @@ public class HdmiControlService extends SystemService { // initPortInfo at hotplug event. mHdmiCecNetwork.initPortInfo(); + HdmiPortInfo portInfo = getPortInfo(portId); if (connected && !isTvDevice() - && getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) { + && portInfo != null && portInfo.getType() == HdmiPortInfo.PORT_OUTPUT) { ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>(); for (int type : getCecLocalDeviceTypes()) { HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type); diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 972f85793556..24e23003d12b 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3511,6 +3511,13 @@ public class InputManagerService extends IInputManager.Stub mNative.setAccessibilityBounceKeysThreshold(thresholdTimeMs); } + /** + * Sets whether Accessibility sticky keys is enabled. + */ + public void setAccessibilityStickyKeysEnabled(boolean enabled) { + mNative.setAccessibilityStickyKeysEnabled(enabled); + } + interface KeyboardBacklightControllerInterface { default void incrementKeyboardBacklight(int deviceId) {} default void decrementKeyboardBacklight(int deviceId) {} diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 0012eab11277..c9668a236f48 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -87,7 +87,9 @@ class InputSettingsObserver extends ContentObserver { Map.entry(Settings.System.getUriFor(Settings.System.SHOW_ROTARY_INPUT), (reason) -> updateShowRotaryInput()), Map.entry(Settings.System.getUriFor(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS), - (reason) -> updateAccessibilityBounceKeys())); + (reason) -> updateAccessibilityBounceKeys()), + Map.entry(Settings.System.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS), + (reason) -> updateAccessibilityStickyKeys())); } /** @@ -223,4 +225,9 @@ class InputSettingsObserver extends ContentObserver { mService.setAccessibilityBounceKeysThreshold( InputSettings.getAccessibilityBounceKeysThreshold(mContext)); } + + private void updateAccessibilityStickyKeys() { + mService.setAccessibilityStickyKeysEnabled( + InputSettings.isAccessibilityStickyKeysEnabled(mContext)); + } } diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index 49bbe9a7655c..829b6607f113 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -251,6 +251,11 @@ interface NativeInputManagerService { */ void setAccessibilityBounceKeysThreshold(int thresholdTimeMs); + /** + * Notify if Accessibility sticky keys is enabled/disabled from InputSettings. + */ + void setAccessibilityStickyKeysEnabled(boolean enabled); + /** The native implementation of InputManagerService methods. */ class NativeImpl implements NativeInputManagerService { /** Pointer to native input manager service object, used by native code. */ @@ -508,5 +513,8 @@ interface NativeInputManagerService { @Override public native void setAccessibilityBounceKeysThreshold(int thresholdTimeMs); + + @Override + public native void setAccessibilityStickyKeysEnabled(boolean enabled); } } diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index d3eb5a95ccdc..91e6a80a5bbb 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -1734,12 +1734,6 @@ public class LocationProviderManager extends return null; } - // lastly - note app ops - if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), - identity)) { - return null; - } - Location location = getPermittedLocation( getLastLocationUnsafe( identity.getUserId(), @@ -1748,10 +1742,18 @@ public class LocationProviderManager extends Long.MAX_VALUE), permissionLevel); - if (location != null && identity.getPid() == Process.myPid()) { + if (location != null) { + // lastly - note app ops + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), + identity)) { + return null; + } + // if delivering to the same process, make a copy of the location first (since // location is mutable) - location = new Location(location); + if (identity.getPid() == Process.myPid()) { + location = new Location(location); + } } return location; diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig index da017453ed8b..55d8a0fd1fa1 100644 --- a/services/core/java/com/android/server/notification/flags.aconfig +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -33,4 +33,11 @@ flag { namespace: "systemui" description: "This flag controls the vibrate while unlocked setting of polite notifications" bug: "270456865" -}
\ No newline at end of file +} + +flag { + name: "autogroup_summary_icon_update" + namespace: "systemui" + description: "This flag controls the fix for notifications autogroup summary icon updates" + bug: "227693160" +} diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 482807c397ea..27f4e11c53ad 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -518,9 +518,7 @@ public interface Computer extends PackageDataSnapshot { boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) throws PackageManager.NameNotFoundException; - /** Check if the package is suspending any package. */ - boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, - @UserIdInt int suspendingUserId, int targetUserId); + boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId); @NonNull ParceledListSlice<IntentFilter> getAllIntentFilters(@NonNull String packageName); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 3cb2420cd223..744c94661067 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -96,7 +96,6 @@ import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.SigningInfo; import android.content.pm.UserInfo; -import android.content.pm.UserPackage; import android.content.pm.VersionedPackage; import android.os.Binder; import android.os.Build; @@ -5009,13 +5008,11 @@ public class ComputerEngine implements Computer { @Override public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, - @UserIdInt int suspendingUserId, int targetUserId) { - final UserPackage suspender = UserPackage.of(suspendingUserId, suspendingPackage); + @UserIdInt int userId) { for (final PackageStateInternal packageState : getPackageStates().values()) { - final PackageUserStateInternal state = - packageState.getUserStateOrDefault(targetUserId); + final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId); if (state.getSuspendParams() != null - && state.getSuspendParams().containsKey(suspender)) { + && state.getSuspendParams().containsKey(suspendingPackage)) { return true; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 8da168375447..c737b45ae885 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -19,8 +19,6 @@ package com.android.server.pm; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.RESTRICTION_NONE; -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -39,7 +37,6 @@ import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.SuspendDialogInfo; -import android.content.pm.UserPackage; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -253,7 +250,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { getSuspendPackageHelper().removeSuspensionsBySuspendingPackage(snapshot(), new String[]{packageName}, (suspendingPackage) -> !PackageManagerService.PLATFORM_PACKAGE_NAME.equals( - suspendingPackage.packageName), + suspendingPackage), userId); } @@ -272,7 +269,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated - public final UserPackage getSuspendingPackage(String suspendedPackage, int userId) { + public final String getSuspendingPackage(String suspendedPackage, int userId) { return getSuspendPackageHelper().getSuspendingPackage(snapshot(), suspendedPackage, userId, Binder.getCallingUid()); } @@ -280,7 +277,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated public final SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, - UserPackage suspendingPackage, int userId) { + String suspendingPackage, int userId) { return getSuspendPackageHelper().getSuspendedDialogInfo(snapshot(), suspendedPackage, suspendingPackage, userId, Binder.getCallingUid()); } @@ -686,16 +683,14 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated - public final void unsuspendAdminSuspendedPackages(int affectedUser) { - final int suspendingUserId = affectedUser; - mService.unsuspendForSuspendingPackage(snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId); + public final void unsuspendForSuspendingPackage(final String packageName, int affectedUser) { + mService.unsuspendForSuspendingPackage(snapshot(), packageName, affectedUser); } @Override @Deprecated - public final boolean isAdminSuspendingAnyPackages(int userId) { - final int suspendingUserId = userId; - return snapshot().isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, suspendingUserId, userId); + public final boolean isSuspendingAnyPackages(String suspendingPackage, int userId) { + return snapshot().isSuspendingAnyPackages(suspendingPackage, userId); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bc441b84c58b..56365b676618 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -292,7 +292,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import java.util.function.Predicate; /** * Keep track of all those APKs everywhere. @@ -3138,7 +3137,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot, - boolean quarantined, UserPackage suspender, int callingUid, int targetUserId, + boolean quarantined, String callingPackage, int callingUid, int userId, String callingMethod) { if (callingUid == Process.ROOT_UID // Need to compare app-id to allow system dialogs access on secondary users @@ -3146,10 +3145,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService return; } - final String ownerPackage = - mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId); + final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId); if (ownerPackage != null) { - final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, targetUserId); + final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, userId); if (ownerUid == callingUid) { return; } @@ -3170,27 +3168,25 @@ public class PackageManagerService implements PackageSender, TestUtilityService callingMethod); } - final int packageUid = snapshot.getPackageUid(suspender.packageName, 0, targetUserId); + final int packageUid = snapshot.getPackageUid(callingPackage, 0, userId); final boolean allowedPackageUid = packageUid == callingUid; // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL final boolean allowedShell = callingUid == SHELL_UID && UserHandle.isSameApp(packageUid, callingUid); if (!allowedShell && !allowedPackageUid) { - throw new SecurityException("Suspending package " + suspender.packageName - + " in user " + targetUserId + " does not belong to calling uid " + callingUid); + throw new SecurityException("Calling package " + callingPackage + " in user " + + userId + " does not belong to calling uid " + callingUid); } } void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage, - @UserIdInt int suspendingUserId) { + @UserIdInt int userId) { // TODO: This can be replaced by a special parameter to iterate all packages, rather than // this weird pre-collect of all packages. final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]); - final Predicate<UserPackage> suspenderPredicate = - UserPackage.of(suspendingUserId, suspendingPackage)::equals; mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, - allPackages, suspenderPredicate, suspendingUserId); + allPackages, suspendingPackage::equals, userId); } void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) { @@ -5263,9 +5259,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (!snapshot.isPackageSuspendedForUser(packageName, userId)) { return null; } - final UserPackage suspender = mSuspendPackageHelper.getSuspendingPackage( - snapshot, packageName, userId, callingUid); - return suspender != null ? suspender.packageName : null; + return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId, + callingUid); } catch (PackageManager.NameNotFoundException e) { return null; } @@ -6203,8 +6198,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, - SuspendDialogInfo dialogInfo, int flags, String suspendingPackage, - int suspendingUserId, int targetUserId) { + SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) { final int callingUid = Binder.getCallingUid(); boolean quarantined = false; if (Flags.quarantinedEnabled()) { @@ -6213,15 +6207,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService } else if (FeatureFlagUtils.isEnabled(mContext, SETTINGS_TREAT_PAUSE_AS_QUARANTINE)) { final String wellbeingPkg = mContext.getString(R.string.config_systemWellbeing); - quarantined = suspendingPackage.equals(wellbeingPkg); + quarantined = callingPackage.equals(wellbeingPkg); } } final Computer snapshot = snapshotComputer(); - final UserPackage suspender = UserPackage.of(targetUserId, suspendingPackage); - enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, suspender, callingUid, - targetUserId, "setPackagesSuspendedAsUser"); + enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, callingPackage, callingUid, + userId, "setPackagesSuspendedAsUser"); return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended, - appExtras, launcherExtras, dialogInfo, suspender, targetUserId, callingUid, + appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid, quarantined); } @@ -6660,7 +6653,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService final Computer computer = snapshotComputer(); final String[] allPackages = computer.getAllAvailablePackageNames(); mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages, - (suspender) -> !PLATFORM_PACKAGE_NAME.equals(suspender.packageName), + (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage), userId); } @@ -6674,13 +6667,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String[] setPackagesSuspendedByAdmin( @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended) { - final int suspendingUserId = userId; - final UserPackage suspender = UserPackage.of( - suspendingUserId, PackageManagerService.PLATFORM_PACKAGE_NAME); - return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames, - suspended, null /* appExtras */, null /* launcherExtras */, - null /* dialogInfo */, suspender, userId, Process.SYSTEM_UID, - false /* quarantined */); + return mSuspendPackageHelper.setPackagesSuspendedByAdmin( + snapshotComputer(), userId, packageNames, suspended); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 243fb16b19ae..322557b9e496 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2827,7 +2827,7 @@ class PackageManagerShellCommand extends ShellCommand { mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}), suspendedState, ((appExtras.size() > 0) ? appExtras : null), ((launcherExtras.size() > 0) ? launcherExtras : null), - info, flags, callingPackage, UserHandle.USER_SYSTEM, translatedUserId); + info, flags, callingPackage, translatedUserId); for (int i = 0; i < packageNames.size(); i++) { final String packageName = packageNames.get(i); pw.println("Package " + packageName + " new suspended state: " diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 28a90f3d6ab6..7d0a1f6afe1d 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -32,7 +32,6 @@ import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; import android.content.pm.SigningInfo; import android.content.pm.UserInfo; -import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.os.UserHandle; import android.os.incremental.IncrementalManager; @@ -953,7 +952,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal void setUserState(int userId, long ceDataInode, long deDataInode, int enabled, boolean installed, boolean stopped, boolean notLaunched, boolean hidden, - int distractionFlags, ArrayMap<UserPackage, SuspendParams> suspendParams, + int distractionFlags, ArrayMap<String, SuspendParams> suspendParams, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet<String> enabledComponents, ArraySet<String> disabledComponents, int installReason, int uninstallReason, @@ -1183,7 +1182,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal if (state.isSuspended()) { for (int j = 0; j < state.getSuspendParams().size(); j++) { proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, - state.getSuspendParams().keyAt(j).packageName); + state.getSuspendParams().keyAt(j)); } } proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.isStopped()); diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 9a7916a7b215..5e8778d3e29c 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -53,7 +53,7 @@ import java.util.Map; * as install) led to the request. */ final class ReconcilePackageUtils { - private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE || true; + private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE; public static List<ReconciledPackage> reconcilePackages( List<InstallRequest> installRequests, diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index cdf1f949a2d8..460bbb7e4b41 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -51,7 +51,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; -import android.content.pm.UserPackage; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.overlay.OverlayPaths; import android.net.Uri; @@ -1957,7 +1956,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile ArchiveState archiveState = null; int packageDepth = parser.getDepth(); - ArrayMap<UserPackage, SuspendParams> suspendParamsMap = null; + ArrayMap<String, SuspendParams> suspendParamsMap = null; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > packageDepth)) { @@ -1984,15 +1983,18 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile parser); break; case TAG_SUSPEND_PARAMS: - Map.Entry<UserPackage, SuspendParams> entry = - readSuspensionParamsLPr(userId, parser); - if (entry == null) { + final String suspendingPackage = parser.getAttributeValue(null, + ATTR_SUSPENDING_PACKAGE); + if (suspendingPackage == null) { + Slog.wtf(TAG, "No suspendingPackage found inside tag " + + TAG_SUSPEND_PARAMS); continue; } if (suspendParamsMap == null) { suspendParamsMap = new ArrayMap<>(); } - suspendParamsMap.put(entry.getKey(), entry.getValue()); + suspendParamsMap.put(suspendingPackage, + SuspendParams.restoreFromXml(parser)); break; case TAG_ARCHIVE_STATE: archiveState = parseArchiveState(parser); @@ -2014,8 +2016,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile oldSuspendedLauncherExtras, false /* quarantined */); suspendParamsMap = new ArrayMap<>(); - suspendParamsMap.put( - UserPackage.of(userId, oldSuspendingPackage), suspendParams); + suspendParamsMap.put(oldSuspendingPackage, suspendParams); } if (blockUninstall) { @@ -2057,20 +2058,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } } - @Nullable - private static Map.Entry<UserPackage, SuspendParams> readSuspensionParamsLPr( - int userId, TypedXmlPullParser parser) throws IOException { - final String suspendingPackage = parser.getAttributeValue(null, ATTR_SUSPENDING_PACKAGE); - if (suspendingPackage == null) { - Slog.wtf(TAG, "No suspendingPackage found inside tag " + TAG_SUSPEND_PARAMS); - return null; - } - final int suspendingUserId = userId; - return Map.entry( - UserPackage.of(suspendingUserId, suspendingPackage), - SuspendParams.restoreFromXml(parser)); - } - private static ArchiveState parseArchiveState(TypedXmlPullParser parser) throws XmlPullParserException, IOException { String installerTitle = parser.getAttributeValue(null, @@ -2427,11 +2414,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } if (ustate.isSuspended()) { for (int i = 0; i < ustate.getSuspendParams().size(); i++) { - final UserPackage suspendingPackage = - ustate.getSuspendParams().keyAt(i); + final String suspendingPackage = ustate.getSuspendParams().keyAt(i); serializer.startTag(null, TAG_SUSPEND_PARAMS); serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, - suspendingPackage.packageName); + suspendingPackage); final SuspendParams params = ustate.getSuspendParams().valueAt(i); if (params != null) { diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index 4e70cc52ef90..c2a960a95394 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -27,10 +27,10 @@ import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.Intent; import android.content.pm.SuspendDialogInfo; -import android.content.pm.UserPackage; import android.os.Binder; import android.os.Bundle; import android.os.PersistableBundle; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; @@ -88,8 +88,8 @@ public final class SuspendPackageHelper { * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that * should be shown to the user when they try to launch a suspended app. * Ignored if {@code suspended} is false. - * @param suspendingPackage The caller's package name. - * @param targetUserId The user where packages reside. + * @param callingPackage The caller's package name. + * @param userId The user where packages reside. * @param callingUid The caller's uid. * @return The names of failed packages. */ @@ -97,14 +97,14 @@ public final class SuspendPackageHelper { String[] setPackagesSuspended(@NonNull Computer snapshot, @Nullable String[] packageNames, boolean suspended, @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo, - @NonNull UserPackage suspendingPackage, @UserIdInt int targetUserId, int callingUid, + @NonNull String callingPackage, @UserIdInt int userId, int callingUid, boolean quarantined) { if (ArrayUtils.isEmpty(packageNames)) { return packageNames; } - if (suspended && !quarantined - && !isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) { - Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId); + if (suspended && !quarantined && !isSuspendAllowedForUser(snapshot, userId, + callingUid)) { + Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId); return packageNames; } @@ -119,21 +119,19 @@ public final class SuspendPackageHelper { final IntArray changedUids = new IntArray(packageNames.length); final boolean[] canSuspend = suspended - ? canSuspendPackageForUser(snapshot, packageNames, targetUserId, callingUid) + ? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid) : null; for (int i = 0; i < packageNames.length; i++) { final String packageName = packageNames[i]; - if (suspendingPackage.packageName.equals(packageName) - && suspendingPackage.userId == targetUserId) { - Slog.w(TAG, "Suspending package: " + suspendingPackage + " trying to " + if (callingPackage.equals(packageName)) { + Slog.w(TAG, "Calling package: " + callingPackage + " trying to " + (suspended ? "" : "un") + "suspend itself. Ignoring"); unmodifiablePackages.add(packageName); continue; } final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); - if (packageState == null - || !packageState.getUserStateOrDefault(targetUserId).isInstalled() - || snapshot.shouldFilterApplication(packageState, callingUid, targetUserId)) { + if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled() + || snapshot.shouldFilterApplication(packageState, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName + ". Skipping suspending/un-suspending."); unmodifiablePackages.add(packageName); @@ -144,34 +142,34 @@ public final class SuspendPackageHelper { continue; } - final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap = - packageState.getUserStateOrDefault(targetUserId).getSuspendParams(); + final WatchedArrayMap<String, SuspendParams> suspendParamsMap = + packageState.getUserStateOrDefault(userId).getSuspendParams(); final SuspendParams oldSuspendParams = suspendParamsMap == null - ? null : suspendParamsMap.get(suspendingPackage); + ? null : suspendParamsMap.get(callingPackage); boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams); if (suspended && !changed) { // Carried over API behavior, must notify change even if no change notifyPackagesList.add(packageName); notifyUids.add( - UserHandle.getUid(targetUserId, packageState.getAppId())); + UserHandle.getUid(userId, packageState.getAppId())); continue; } - // If only the suspendingPackage is suspending this package, + // If only the callingPackage is suspending this package, // it will be unsuspended when this change is committed boolean packageUnsuspended = !suspended && CollectionUtils.size(suspendParamsMap) == 1 - && suspendParamsMap.containsKey(suspendingPackage); + && suspendParamsMap.containsKey(callingPackage); if (suspended || packageUnsuspended) { // Always notify of a suspend call + notify when fully unsuspended notifyPackagesList.add(packageName); - notifyUids.add(UserHandle.getUid(targetUserId, packageState.getAppId())); + notifyUids.add(UserHandle.getUid(userId, packageState.getAppId())); } if (changed) { changedPackagesList.add(packageName); - changedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId())); + changedUids.add(UserHandle.getUid(userId, packageState.getAppId())); } else { Slog.w(TAG, "No change is needed for package: " + packageName + ". Skipping suspending/un-suspending."); @@ -183,11 +181,11 @@ public final class SuspendPackageHelper { for (int index = 0; index < size; index++) { final String packageName = changedPackagesList.valueAt(index); final PackageUserStateWrite userState = mutator.forPackage(packageName) - .userState(targetUserId); + .userState(userId); if (suspended) { - userState.putSuspendParams(suspendingPackage, newSuspendParams); + userState.putSuspendParams(callingPackage, newSuspendParams); } else { - userState.removeSuspension(suspendingPackage); + userState.removeSuspension(callingPackage); } } }); @@ -199,17 +197,17 @@ public final class SuspendPackageHelper { mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, - changedPackages, notifyUids.toArray(), quarantined, targetUserId); + changedPackages, notifyUids.toArray(), quarantined, userId); mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages, - suspended, targetUserId); - mPm.scheduleWritePackageRestrictions(targetUserId); + suspended, userId); + mPm.scheduleWritePackageRestrictions(userId); } // Send the suspension changed broadcast to ensure suspension state is not stale. if (!changedPackagesList.isEmpty()) { mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined, - targetUserId); + userId); } return unmodifiablePackages.toArray(new String[0]); } @@ -218,19 +216,19 @@ public final class SuspendPackageHelper { * Returns the names in the {@code packageNames} which can not be suspended by the caller. * * @param packageNames The names of packages to check. - * @param targetUserId The user where packages reside. + * @param userId The user where packages reside. * @param callingUid The caller's uid. * @return The names of packages which are Unsuspendable. */ @NonNull String[] getUnsuspendablePackagesForUser(@NonNull Computer snapshot, - @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid) { - if (!isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) { - Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId); + @NonNull String[] packageNames, @UserIdInt int userId, int callingUid) { + if (!isSuspendAllowedForUser(snapshot, userId, callingUid)) { + Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId); return packageNames; } final ArraySet<String> unactionablePackages = new ArraySet<>(); - final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, targetUserId, + final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, userId, callingUid); for (int i = 0; i < packageNames.length; i++) { if (!canSuspend[i]) { @@ -239,7 +237,7 @@ public final class SuspendPackageHelper { } final PackageStateInternal packageState = snapshot.getPackageStateForInstalledAndFiltered( - packageNames[i], callingUid, targetUserId); + packageNames[i], callingUid, userId); if (packageState == null) { Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]); unactionablePackages.add(packageNames[i]); @@ -287,31 +285,30 @@ public final class SuspendPackageHelper { * @param packagesToChange The packages on which the suspension are to be removed. * @param suspendingPackagePredicate A predicate identifying the suspending packages whose * suspensions will be removed. - * @param targetUserId The user for which the changes are taking place. + * @param userId The user for which the changes are taking place. */ void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot, @NonNull String[] packagesToChange, - @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId) { + @NonNull Predicate<String> suspendingPackagePredicate, int userId) { final List<String> unsuspendedPackages = new ArrayList<>(); final IntArray unsuspendedUids = new IntArray(); - final ArrayMap<String, ArraySet<UserPackage>> pkgToSuspendingPkgsToCommit = - new ArrayMap<>(); + final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>(); for (String packageName : packagesToChange) { final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); final PackageUserStateInternal packageUserState = packageState == null - ? null : packageState.getUserStateOrDefault(targetUserId); + ? null : packageState.getUserStateOrDefault(userId); if (packageUserState == null || !packageUserState.isSuspended()) { continue; } - WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap = + WatchedArrayMap<String, SuspendParams> suspendParamsMap = packageUserState.getSuspendParams(); int countRemoved = 0; for (int index = 0; index < suspendParamsMap.size(); index++) { - UserPackage suspendingPackage = suspendParamsMap.keyAt(index); + String suspendingPackage = suspendParamsMap.keyAt(index); if (suspendingPackagePredicate.test(suspendingPackage)) { - ArraySet<UserPackage> suspendingPkgsToCommit = + ArraySet<String> suspendingPkgsToCommit = pkgToSuspendingPkgsToCommit.get(packageName); if (suspendingPkgsToCommit == null) { suspendingPkgsToCommit = new ArraySet<>(); @@ -325,33 +322,31 @@ public final class SuspendPackageHelper { // Everything would be removed and package unsuspended if (countRemoved == suspendParamsMap.size()) { unsuspendedPackages.add(packageState.getPackageName()); - unsuspendedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId())); + unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId())); } } mPm.commitPackageStateMutation(null, mutator -> { for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) { String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex); - ArraySet<UserPackage> packagesToRemove = - pkgToSuspendingPkgsToCommit.valueAt(mapIndex); - PackageUserStateWrite userState = - mutator.forPackage(packageName).userState(targetUserId); + ArraySet<String> packagesToRemove = pkgToSuspendingPkgsToCommit.valueAt(mapIndex); + PackageUserStateWrite userState = mutator.forPackage(packageName).userState(userId); for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) { userState.removeSuspension(packagesToRemove.valueAt(setIndex)); } } }); - mPm.scheduleWritePackageRestrictions(targetUserId); + mPm.scheduleWritePackageRestrictions(userId); final Computer newSnapshot = mPm.snapshotComputer(); if (!unsuspendedPackages.isEmpty()) { final String[] packageArray = unsuspendedPackages.toArray( new String[unsuspendedPackages.size()]); mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray, - false, targetUserId); + false, userId); mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, Intent.ACTION_PACKAGES_UNSUSPENDED, - packageArray, unsuspendedUids.toArray(), false, targetUserId); + packageArray, unsuspendedUids.toArray(), false, userId); } } @@ -409,7 +404,7 @@ public final class SuspendPackageHelper { * @return The name of suspending package. */ @Nullable - UserPackage getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage, + String getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage, int userId, int callingUid) { final PackageStateInternal packageState = snapshot.getPackageStateInternal( suspendedPackage, callingUid); @@ -422,13 +417,13 @@ public final class SuspendPackageHelper { return null; } - UserPackage suspendingPackage = null; - UserPackage suspendedBySystem = null; - UserPackage qasPackage = null; + String suspendingPackage = null; + String suspendedBySystem = null; + String qasPackage = null; for (int i = 0; i < userState.getSuspendParams().size(); i++) { suspendingPackage = userState.getSuspendParams().keyAt(i); var suspendParams = userState.getSuspendParams().valueAt(i); - if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) { + if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { suspendedBySystem = suspendingPackage; } if (suspendParams.isQuarantined() && qasPackage == null) { @@ -456,7 +451,7 @@ public final class SuspendPackageHelper { */ @Nullable SuspendDialogInfo getSuspendedDialogInfo(@NonNull Computer snapshot, - @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId, + @NonNull String suspendedPackage, @NonNull String suspendingPackage, int userId, int callingUid) { final PackageStateInternal packageState = snapshot.getPackageStateInternal( suspendedPackage, callingUid); @@ -469,7 +464,7 @@ public final class SuspendPackageHelper { return null; } - final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap = + final WatchedArrayMap<String, SuspendParams> suspendParamsMap = userState.getSuspendParams(); if (suspendParamsMap == null) { return null; @@ -498,36 +493,34 @@ public final class SuspendPackageHelper { * be suspended or not. * * @param packageNames The package names to check suspendability for. - * @param targetUserId The user to check in + * @param userId The user to check in * @param callingUid The caller's uid. * @return An array containing results of the checks */ @NonNull boolean[] canSuspendPackageForUser(@NonNull Computer snapshot, @NonNull String[] packageNames, - int targetUserId, int callingUid) { + int userId, int callingUid) { final boolean[] canSuspend = new boolean[packageNames.length]; - final boolean isCallerOwner = - isCallerDeviceOrProfileOwner(snapshot, targetUserId, callingUid); + final boolean isCallerOwner = isCallerDeviceOrProfileOwner(snapshot, userId, callingUid); final long token = Binder.clearCallingIdentity(); try { final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider(); - final String activeLauncherPackageName = - defaultAppProvider.getDefaultHome(targetUserId); - final String dialerPackageName = defaultAppProvider.getDefaultDialer(targetUserId); + final String activeLauncherPackageName = defaultAppProvider.getDefaultHome(userId); + final String dialerPackageName = defaultAppProvider.getDefaultDialer(userId); final String requiredInstallerPackage = - getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, targetUserId); + getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, userId); final String requiredUninstallerPackage = - getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, targetUserId); + getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, userId); final String requiredVerifierPackage = - getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, targetUserId); + getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, userId); final String requiredPermissionControllerPackage = getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER, - targetUserId); + userId); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; final String packageName = packageNames[i]; - if (mPm.isPackageDeviceAdmin(packageName, targetUserId)) { + if (mPm.isPackageDeviceAdmin(packageName, userId)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": has an active device admin"); continue; @@ -562,12 +555,12 @@ public final class SuspendPackageHelper { + "\": required for permissions management"); continue; } - if (mProtectedPackages.isPackageStateProtected(targetUserId, packageName)) { + if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": protected package"); continue; } - if (!isCallerOwner && snapshot.getBlockUninstall(targetUserId, packageName)) { + if (!isCallerOwner && snapshot.getBlockUninstall(userId, packageName)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": blocked by admin"); continue; @@ -579,7 +572,7 @@ public final class SuspendPackageHelper { PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); AndroidPackage pkg = packageState == null ? null : packageState.getPkg(); if (pkg != null) { - final int uid = UserHandle.getUid(targetUserId, packageState.getAppId()); + final int uid = UserHandle.getUid(userId, packageState.getAppId()); // Cannot suspend SDK libs as they are controlled by SDK manager. if (pkg.isSdkLibrary()) { Slog.w(TAG, "Cannot suspend package: " + packageName @@ -621,6 +614,20 @@ public final class SuspendPackageHelper { == AppOpsManager.MODE_ALLOWED; } + /** + * Suspends packages on behalf of an admin. + * + * @return array of packages that are unsuspendable, either because admin is not allowed to + * suspend them (e.g. current dialer) or there was other problem (e.g. package not found). + */ + public String[] setPackagesSuspendedByAdmin( + Computer snapshot, int userId, String[] packageNames, boolean suspend) { + return setPackagesSuspended(snapshot, packageNames, suspend, + null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, + PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID, + false /* quarantined */); + } + private String getKnownPackageName(@NonNull Computer snapshot, @KnownPackages.KnownPackage int knownPackage, int userId) { final String[] knownPackages = @@ -628,15 +635,14 @@ public final class SuspendPackageHelper { return knownPackages.length > 0 ? knownPackages[0] : null; } - private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int targetUserId, + private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int userId, int callingUid) { if (callingUid == SYSTEM_UID) { return true; } - final String ownerPackage = - mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId); + final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId); if (ownerPackage != null) { - return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, targetUserId, + return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, userId, callingUid); } return false; diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java index 15b693cf72f8..2f4ad2d8fcc6 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; -import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.util.ArraySet; import android.util.Pair; @@ -174,7 +173,7 @@ class PackageUserStateDefault implements PackageUserStateInternal { @Nullable @Override - public WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams() { + public WatchedArrayMap<String, SuspendParams> getSuspendParams() { return null; } diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java index 7a5a14d8d3c2..c5ef5257ddd5 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; -import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.text.TextUtils; import android.util.ArrayMap; @@ -122,7 +121,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ @Nullable - private WatchedArrayMap<UserPackage, SuspendParams> mSuspendParams; + private WatchedArrayMap<String, SuspendParams> mSuspendParams; @Nullable private WatchedArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap; @@ -370,10 +369,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt return !CollectionUtils.isEmpty(mSuspendParams); } - /** - * Adds or updates suspension params by the given package. - */ - public PackageUserStateImpl putSuspendParams(@NonNull UserPackage suspendingPackage, + public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage, @Nullable SuspendParams suspendParams) { if (mSuspendParams == null) { mSuspendParams = new WatchedArrayMap<>(); @@ -388,10 +384,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt return this; } - /** - * Removes suspension by the given package. - */ - public PackageUserStateImpl removeSuspension(@NonNull UserPackage suspendingPackage) { + public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) { if (mSuspendParams != null) { mSuspendParams.remove(suspendingPackage); onChanged(); @@ -572,7 +565,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ public @NonNull PackageUserStateImpl setSuspendParams( - @NonNull ArrayMap<UserPackage, SuspendParams> value) { + @NonNull ArrayMap<String, SuspendParams> value) { if (value == null) { return this; } @@ -785,7 +778,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ @DataClass.Generated.Member - public @Nullable WatchedArrayMap<UserPackage,SuspendParams> getSuspendParams() { + public @Nullable WatchedArrayMap<String,SuspendParams> getSuspendParams() { return mSuspendParams; } @@ -837,7 +830,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<UserPackage,SuspendParams> value) { + public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<String,SuspendParams> value) { mSuspendParams = value; return this; } @@ -916,10 +909,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt } @DataClass.Generated( - time = 1701864813354L, + time = 1701470095849L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java", - inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(android.content.pm.UserPackage)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)") + inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java index f8d745cb7fbf..46cc830130ef 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java @@ -19,7 +19,6 @@ package com.android.server.pm.pkg; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; -import android.content.pm.UserPackage; import android.content.pm.pkg.FrameworkPackageUserState; import android.util.Pair; @@ -39,7 +38,7 @@ public interface PackageUserStateInternal extends PackageUserState, FrameworkPac // TODO: Make non-null with emptyMap() @Nullable - WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams(); + WatchedArrayMap<String, SuspendParams> getSuspendParams(); @Nullable WatchedArraySet<String> getDisabledComponentsNoCopy(); diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java index 253eb4006122..8430cf7a0d11 100644 --- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java +++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.util.ArraySet; @@ -350,7 +349,7 @@ public class PackageStateMutator { @NonNull @Override - public PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage, + public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage, @Nullable SuspendParams suspendParams) { if (mUserState != null) { mUserState.putSuspendParams(suspendingPackage, suspendParams); @@ -360,7 +359,7 @@ public class PackageStateMutator { @NonNull @Override - public PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage) { + public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) { if (mUserState != null) { mUserState.removeSuspension(suspendingPackage); } diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java index f6b21045a8bb..0c6c6723b79b 100644 --- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java +++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; -import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import com.android.server.pm.pkg.PackageUserStateImpl; @@ -39,11 +38,11 @@ public interface PackageUserStateWrite { @PackageManager.DistractionRestriction int restrictionFlags); @NonNull - PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage, + PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage, @Nullable SuspendParams suspendParams); @NonNull - PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage); + PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage); @NonNull PackageUserStateWrite setHidden(boolean hidden); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2df965c5a83f..f3922f9917ba 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -182,6 +182,7 @@ import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS; import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN; import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE; import static com.android.server.wm.ActivityRecordProto.SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT; +import static com.android.server.wm.ActivityRecordProto.SHOULD_OVERRIDE_MIN_ASPECT_RATIO; import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT; import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT; import static com.android.server.wm.ActivityRecordProto.SHOULD_SEND_COMPAT_FAKE_FOCUS; @@ -10357,6 +10358,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLetterboxUiController.shouldRefreshActivityForCameraCompat()); proto.write(SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT, mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat()); + proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO, + mLetterboxUiController.shouldOverrideMinAspectRatio()); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 1b45c1b4f3f1..f9d344bd7e31 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -48,7 +48,6 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; -import android.content.pm.UserPackage; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -336,19 +335,19 @@ class ActivityStartInterceptor { return false; } final String suspendedPackage = mAInfo.applicationInfo.packageName; - final UserPackage suspender = pmi.getSuspendingPackage(suspendedPackage, mUserId); - if (suspender != null && PLATFORM_PACKAGE_NAME.equals(suspender.packageName)) { + final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId); + if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { return interceptSuspendedByAdminPackage(); } final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, - suspender, mUserId); + suspendingPackage, mUserId); final Bundle crossProfileOptions = hasCrossProfileAnimation() ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle() : null; final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, FLAG_IMMUTABLE); mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage, - suspender, dialogInfo, crossProfileOptions, target, mUserId); + suspendingPackage, dialogInfo, crossProfileOptions, target, mUserId); mCallingPid = mRealCallingPid; mCallingUid = mRealCallingUid; mResolvedType = null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 869bcc0f9b8f..38682caaa036 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3695,6 +3695,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { boolean originallyFromClient = fromClient && (!r.isState(PAUSING) || params.isAutoEnterEnabled()); + // If PiP2 flag is on and client-request to enter PiP came via onUserLeaveHint(), + // we request a direct transition from Shell to TRANSIT_PIP_LEGACY to get the startWct + // with the right entry bounds. + if (isPip2ExperimentEnabled() && !originallyFromClient && !params.isAutoEnterEnabled()) { + final Transition legacyEnterPipTransition = new Transition(TRANSIT_PIP, + 0 /* flags */, getTransitionController(), + mWindowManager.mSyncEngine); + legacyEnterPipTransition.setPipActivity(r); + getTransitionController().startCollectOrQueue(legacyEnterPipTransition, (deferred) -> { + getTransitionController().requestStartTransition(legacyEnterPipTransition, + r.getTask(), null /* remoteTransition */, null /* displayChange */); + }); + return true; + } + // Create a transition only for this pip entry if it is coming from the app without the // system requesting that the app enter-pip. If the system requested it, that means it // should be part of that transition if possible. diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java index e91857f1da82..735c73a990cb 100644 --- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java +++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java @@ -171,12 +171,14 @@ public class DimmerAnimationHelper { mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t, ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> { - setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t); - if (targetAlpha == 0f && !dim.isDimming()) { - dim.remove(t); + synchronized (dim.mHostContainer.mWmService.mGlobalLock) { + setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t); + if (targetAlpha == 0f && !dim.isDimming()) { + dim.remove(t); + } + mLocalAnimationAdapter = null; + mAlphaAnimationSpec = null; } - mLocalAnimationAdapter = null; - mAlphaAnimationSpec = null; }); } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index e648d6417e88..c3aca6fe84d1 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -731,9 +731,9 @@ class TransitionController { } // set the pip task in the request if provided - if (mCollectingTransition.getPipActivity() != null) { - pipTaskInfo = mCollectingTransition.getPipActivity().getTask().getTaskInfo(); - mCollectingTransition.setPipActivity(null); + if (transition.getPipActivity() != null) { + pipTaskInfo = transition.getPipActivity().getTask().getTaskInfo(); + transition.setPipActivity(null); } final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType, diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index bc05e77171bd..0dd0564ae034 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -2747,6 +2747,14 @@ static void nativeSetAccessibilityBounceKeysThreshold(JNIEnv* env, jobject nativ } } +static void nativeSetAccessibilityStickyKeysEnabled(JNIEnv* env, jobject nativeImplObj, + jboolean enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); + if (ENABLE_INPUT_FILTER_RUST) { + im->getInputManager()->getInputFilter().setAccessibilityStickyKeysEnabled(enabled); + } +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { @@ -2848,6 +2856,8 @@ static const JNINativeMethod gInputManagerMethods[] = { {"setStylusPointerIconEnabled", "(Z)V", (void*)nativeSetStylusPointerIconEnabled}, {"setAccessibilityBounceKeysThreshold", "(I)V", (void*)nativeSetAccessibilityBounceKeysThreshold}, + {"setAccessibilityStickyKeysEnabled", "(Z)V", + (void*)nativeSetAccessibilityStickyKeysEnabled}, }; #define FIND_CLASS(var, className) \ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a490013303e9..e0a2f30b1831 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2469,7 +2469,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void migratePersonalAppSuspensionLocked( int doUserId, int poUserId, ActiveAdmin poAdmin) { final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); - if (!pmi.isAdminSuspendingAnyPackages(doUserId)) { + if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) { Slogf.i(LOG_TAG, "DO is not suspending any apps."); return; } @@ -2480,7 +2480,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { poAdmin.mSuspendPersonalApps = true; } else { Slogf.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps."); - pmi.unsuspendAdminSuspendedPackages(doUserId); + pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index 506dbe8c48c4..6570ce1cd500 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -16,6 +16,8 @@ package com.android.server.devicepolicy; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; @@ -285,7 +287,7 @@ final class PolicyEnforcerCallbacks { suspendPersonalAppsInPackageManager(context, userId); } else { LocalServices.getService(PackageManagerInternal.class) - .unsuspendAdminSuspendedPackages(userId); + .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, userId); } }); return true; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java index 40d3d5ca9fd9..3d0b389aa171 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -50,7 +50,6 @@ import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; -import android.content.pm.UserPackage; import android.os.BaseBundle; import android.os.Message; import android.os.PersistableBundle; @@ -426,7 +425,7 @@ public class PackageManagerSettingsTests { PackageUserStateInternal packageUserState1 = ps1.readUserState(0); assertThat(packageUserState1.isSuspended(), is(true)); assertThat(packageUserState1.getSuspendParams().size(), is(1)); - assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android"))); + assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android")); assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue())); assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(), is(nullValue())); @@ -438,7 +437,7 @@ public class PackageManagerSettingsTests { packageUserState1 = ps1.readUserState(0); assertThat(packageUserState1.isSuspended(), is(true)); assertThat(packageUserState1.getSuspendParams().size(), is(1)); - assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android"))); + assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android")); assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue())); assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(), is(nullValue())); @@ -473,8 +472,7 @@ public class PackageManagerSettingsTests { watcher.verifyNoChangeReported("readUserState"); assertThat(packageUserState1.isSuspended(), is(true)); assertThat(packageUserState1.getSuspendParams().size(), is(1)); - assertThat(packageUserState1.getSuspendParams().keyAt(0), - is(UserPackage.of(0, PACKAGE_NAME_3))); + assertThat(packageUserState1.getSuspendParams().keyAt(0), is(PACKAGE_NAME_3)); final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0); watcher.verifyNoChangeReported("fetch user state"); assertThat(params, is(notNullValue())); @@ -525,24 +523,19 @@ public class PackageManagerSettingsTests { .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND) .build(); - UserPackage suspender1 = UserPackage.of(0, "suspendingPackage1"); - UserPackage suspender2 = UserPackage.of(0, "suspendingPackage2"); - UserPackage suspender3 = UserPackage.of(0, "suspendingPackage3"); - UserPackage irrelevantSuspender = UserPackage.of(0, "irrelevant"); - - ps1.modifyUserState(0).putSuspendParams(suspender1, + ps1.modifyUserState(0).putSuspendParams("suspendingPackage1", new SuspendParams(dialogInfo1, appExtras1, launcherExtras1)); - ps1.modifyUserState(0).putSuspendParams(suspender2, + ps1.modifyUserState(0).putSuspendParams("suspendingPackage2", new SuspendParams(dialogInfo2, appExtras2, launcherExtras2)); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1); watcher.verifyChangeReported("put package 1"); - ps2.modifyUserState(0).putSuspendParams(suspender3, + ps2.modifyUserState(0).putSuspendParams("suspendingPackage3", new SuspendParams(null, appExtras1, null)); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2); watcher.verifyChangeReported("put package 2"); - ps3.modifyUserState(0).removeSuspension(irrelevantSuspender); + ps3.modifyUserState(0).removeSuspension("irrelevant"); settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3); watcher.verifyChangeReported("put package 3"); @@ -567,7 +560,7 @@ public class PackageManagerSettingsTests { assertThat(readPus1.getSuspendParams().size(), is(2)); watcher.verifyNoChangeReported("read package param"); - assertThat(readPus1.getSuspendParams().keyAt(0), is(suspender1)); + assertThat(readPus1.getSuspendParams().keyAt(0), is("suspendingPackage1")); final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0); watcher.verifyNoChangeReported("read package param"); assertThat(params11, is(notNullValue())); @@ -577,7 +570,7 @@ public class PackageManagerSettingsTests { is(true)); watcher.verifyNoChangeReported("read package param"); - assertThat(readPus1.getSuspendParams().keyAt(1), is(suspender2)); + assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2")); final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1); assertThat(params12, is(notNullValue())); assertThat(params12.getDialogInfo(), is(dialogInfo2)); @@ -590,7 +583,7 @@ public class PackageManagerSettingsTests { .readUserState(0); assertThat(readPus2.isSuspended(), is(true)); assertThat(readPus2.getSuspendParams().size(), is(1)); - assertThat(readPus2.getSuspendParams().keyAt(0), is(suspender3)); + assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3")); final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0); assertThat(params21, is(notNullValue())); assertThat(params21.getDialogInfo(), is(nullValue())); @@ -1141,8 +1134,7 @@ public class PackageManagerSettingsTests { .setNeutralButtonText(0x11220003) .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS) .build(); - origPkgSetting01.modifyUserState(0).putSuspendParams( - UserPackage.of(0, "suspendingPackage1"), + origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1", new SuspendParams(dialogInfo1, appExtras1, launcherExtras1)); origPkgSetting01.setPkg(mockAndroidPackage(origPkgSetting01)); final PackageSetting testPkgSetting01 = new PackageSetting( diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java index 978044045ab3..c0c70321c79b 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java @@ -27,7 +27,6 @@ import static org.junit.Assert.assertTrue; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.SuspendDialogInfo; -import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.os.PersistableBundle; import android.platform.test.annotations.Presubmit; @@ -90,7 +89,7 @@ public class PackageUserStateTest { assertThat(testUserState.equals(oldUserState), is(false)); oldUserState = new PackageUserStateImpl(); - oldUserState.putSuspendParams(UserPackage.of(0, "suspendingPackage"), + oldUserState.putSuspendParams("suspendingPackage", new SuspendParams(null, new PersistableBundle(), null)); assertThat(testUserState.equals(oldUserState), is(false)); @@ -221,8 +220,6 @@ public class PackageUserStateTest { final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name", "launcherExtras2", null, 0); - final int suspendingUser1 = 0; - final int suspendingUser2 = 10; final String suspendingPackage1 = "package1"; final String suspendingPackage2 = "package2"; @@ -233,12 +230,12 @@ public class PackageUserStateTest { .setMessage("dialogMessage2") .build(); - final ArrayMap<UserPackage, SuspendParams> paramsMap1 = new ArrayMap<>(); - paramsMap1.put(UserPackage.of(suspendingUser1, suspendingPackage1), - createSuspendParams(dialogInfo1, appExtras1, launcherExtras1)); - final ArrayMap<UserPackage, SuspendParams> paramsMap2 = new ArrayMap<>(); - paramsMap2.put(UserPackage.of(suspendingUser2, suspendingPackage2), - createSuspendParams(dialogInfo2, appExtras2, launcherExtras2)); + final ArrayMap<String, SuspendParams> paramsMap1 = new ArrayMap<>(); + paramsMap1.put(suspendingPackage1, createSuspendParams(dialogInfo1, appExtras1, + launcherExtras1)); + final ArrayMap<String, SuspendParams> paramsMap2 = new ArrayMap<>(); + paramsMap2.put(suspendingPackage2, createSuspendParams(dialogInfo2, + appExtras2, launcherExtras2)); final PackageUserStateImpl testUserState1 = new PackageUserStateImpl(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 2bdebe26e551..418b78cd696b 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -17,6 +17,8 @@ package com.android.server.display; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -39,7 +41,9 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; +import android.os.PowerManager; import android.os.test.TestLooper; +import android.util.SparseArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -98,8 +102,8 @@ public class AutomaticBrightnessControllerTest { mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); mContext = InstrumentationRegistry.getContext(); - setupController(BrightnessMappingStrategy.NO_USER_LUX, - BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ false, + setupController(BrightnessMappingStrategy.INVALID_LUX, + BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ false, /* useHorizon= */ true); } @@ -112,11 +116,19 @@ public class AutomaticBrightnessControllerTest { } } - private void setupController(float userLux, float userBrightness, boolean applyDebounce, + private void setupController(float userLux, float userNits, boolean applyDebounce, boolean useHorizon) { mClock = new OffsettableClock.Stopped(); mTestLooper = new TestLooper(mClock::now); + when(mBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_DEFAULT); + when(mIdleBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_IDLE); + + SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap = new SparseArray<>(); + brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_DEFAULT, + mBrightnessMappingStrategy); + brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_IDLE, + mIdleBrightnessMappingStrategy); mController = new AutomaticBrightnessController( new AutomaticBrightnessController.Injector() { @Override @@ -131,7 +143,7 @@ public class AutomaticBrightnessControllerTest { }, // pass in test looper instead, pass in offsettable clock () -> { }, mTestLooper.getLooper(), mSensorManager, mLightSensor, - mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT, + brightnessMappingStrategyMap, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT, BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE, applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG : 0, applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG : 0, @@ -141,8 +153,8 @@ public class AutomaticBrightnessControllerTest { mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, mBrightnessThrottler, - mIdleBrightnessMappingStrategy, useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1, - useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userBrightness + useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1, + useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits ); when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn( @@ -326,8 +338,7 @@ public class AutomaticBrightnessControllerTest { when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); - mController.switchToIdleMode(); - when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); when(mBrightnessMappingStrategy.shouldResetShortTermModel( 123f, 0.5f)).thenReturn(true); @@ -337,7 +348,7 @@ public class AutomaticBrightnessControllerTest { mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000); mTestLooper.dispatchAll(); - mController.switchToInteractiveScreenBrightnessMode(); + mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); mTestLooper.moveTimeForward(4000); mTestLooper.dispatchAll(); @@ -371,15 +382,14 @@ public class AutomaticBrightnessControllerTest { when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.51f); when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123.0f); - mController.switchToIdleMode(); - when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); // Time does not move forward, since clock is doesn't increment naturally. mTestLooper.dispatchAll(); // Sensor reads 100000 lux, listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 678910)); - mController.switchToInteractiveScreenBrightnessMode(); + mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); // Verify short term model is not reset. verify(mBrightnessMappingStrategy, never()).clearUserDataPoints(); @@ -410,10 +420,11 @@ public class AutomaticBrightnessControllerTest { when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f); when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f); - mController.switchToIdleMode(); - when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); - when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f); - when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f); + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); + when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn( + PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn( + BrightnessMappingStrategy.INVALID_LUX); when(mBrightnessMappingStrategy.shouldResetShortTermModel( 123f, 0.5f)).thenReturn(true); @@ -423,7 +434,7 @@ public class AutomaticBrightnessControllerTest { mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000); mTestLooper.dispatchAll(); - mController.switchToInteractiveScreenBrightnessMode(); + mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); mTestLooper.moveTimeForward(4000); mTestLooper.dispatchAll(); @@ -456,10 +467,11 @@ public class AutomaticBrightnessControllerTest { when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f); when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f); - mController.switchToIdleMode(); - when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); - when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f); - when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f); + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); + when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn( + PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn( + BrightnessMappingStrategy.INVALID_LUX); when(mBrightnessMappingStrategy.shouldResetShortTermModel( 123f, 0.5f)).thenReturn(true); @@ -469,7 +481,7 @@ public class AutomaticBrightnessControllerTest { // Do not fast-forward time. mTestLooper.dispatchAll(); - mController.switchToInteractiveScreenBrightnessMode(); + mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); // Do not fast-forward time mTestLooper.dispatchAll(); @@ -492,22 +504,25 @@ public class AutomaticBrightnessControllerTest { // Sensor reads 123 lux, listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); - when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1.0f); - when(mBrightnessMappingStrategy.getUserLux()).thenReturn(-1.0f); + when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn( + PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mBrightnessMappingStrategy.getUserLux()).thenReturn( + BrightnessMappingStrategy.INVALID_LUX); // No user brightness interaction. - mController.switchToIdleMode(); - when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); - when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1.0f); - when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1.0f); + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); + when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn( + PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn( + BrightnessMappingStrategy.INVALID_LUX); // Sensor reads 1000 lux, listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); // Do not fast-forward time. mTestLooper.dispatchAll(); - mController.switchToInteractiveScreenBrightnessMode(); + mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT); // Do not fast-forward time mTestLooper.dispatchAll(); @@ -541,12 +556,12 @@ public class AutomaticBrightnessControllerTest { verify(mBrightnessMappingStrategy, times(3)).getBrightness(anyFloat(), any(), anyInt()); // Now let's do the same for idle mode - mController.switchToIdleMode(); - // Called once for init, and once when switching, + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); + // Called once when switching, // setAmbientLux() is called twice and once in updateAutoBrightness(), // nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are // called twice each. - verify(mBrightnessMappingStrategy, times(9)).isForIdleMode(); + verify(mBrightnessMappingStrategy, times(8)).getMode(); // Called when switching. verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout(); verify(mBrightnessMappingStrategy, times(1)).getUserBrightness(); @@ -826,7 +841,6 @@ public class AutomaticBrightnessControllerTest { @Test public void testResetShortTermModelWhenConfigChanges() { - when(mBrightnessMappingStrategy.isForIdleMode()).thenReturn(false); when(mBrightnessMappingStrategy.setBrightnessConfiguration(any())).thenReturn(true); mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, @@ -847,8 +861,10 @@ public class AutomaticBrightnessControllerTest { verify(mBrightnessMappingStrategy, never()).addUserDataPoint(anyFloat(), anyFloat()); float userLux = 1000; + float userNits = 500; float userBrightness = 0.3f; - setupController(userLux, userBrightness, /* applyDebounce= */ true, + when(mBrightnessMappingStrategy.getBrightnessFromNits(userNits)).thenReturn(userBrightness); + setupController(userLux, userNits, /* applyDebounce= */ true, /* useHorizon= */ false); verify(mBrightnessMappingStrategy).addUserDataPoint(userLux, userBrightness); } @@ -856,8 +872,8 @@ public class AutomaticBrightnessControllerTest { @Test public void testBrighteningLightDebounce() throws Exception { clearInvocations(mSensorManager); - setupController(BrightnessMappingStrategy.NO_USER_LUX, - BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true, + setupController(BrightnessMappingStrategy.INVALID_LUX, + BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true, /* useHorizon= */ false); ArgumentCaptor<SensorEventListener> listenerCaptor = @@ -897,8 +913,8 @@ public class AutomaticBrightnessControllerTest { .thenReturn(10000f); when(mAmbientBrightnessThresholds.getDarkeningThreshold(anyFloat())) .thenReturn(10000f); - setupController(BrightnessMappingStrategy.NO_USER_LUX, - BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true, + setupController(BrightnessMappingStrategy.INVALID_LUX, + BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true, /* useHorizon= */ false); ArgumentCaptor<SensorEventListener> listenerCaptor = @@ -934,12 +950,11 @@ public class AutomaticBrightnessControllerTest { @Test public void testBrighteningLightDebounceIdle() throws Exception { clearInvocations(mSensorManager); - setupController(BrightnessMappingStrategy.NO_USER_LUX, - BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true, + setupController(BrightnessMappingStrategy.INVALID_LUX, + BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true, /* useHorizon= */ false); - mController.switchToIdleMode(); - when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); @@ -972,12 +987,11 @@ public class AutomaticBrightnessControllerTest { .thenReturn(10000f); when(mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(anyFloat())) .thenReturn(10000f); - setupController(BrightnessMappingStrategy.NO_USER_LUX, - BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true, + setupController(BrightnessMappingStrategy.INVALID_LUX, + BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true, /* useHorizon= */ false); - mController.switchToIdleMode(); - when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java index a2e80f0d9b9b..189d9bbfe806 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -16,6 +16,9 @@ package com.android.server.display; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; @@ -172,7 +175,9 @@ public class BrightnessMappingStrategyTest { public void testSimpleStrategyMappingAtControlPoints_IntConfig() { Resources res = createResources(DISPLAY_LEVELS_INT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 0; i < LUX_LEVELS.length; i++) { final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1, @@ -187,7 +192,9 @@ public class BrightnessMappingStrategyTest { public void testSimpleStrategyMappingBetweenControlPoints_IntConfig() { Resources res = createResources(DISPLAY_LEVELS_INT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 1; i < LUX_LEVELS.length; i++) { final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; @@ -203,7 +210,9 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY); DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS, EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 0; i < LUX_LEVELS.length; i++) { assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]), @@ -216,7 +225,9 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY); DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS, EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 1; i < LUX_LEVELS.length; i++) { final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; @@ -230,7 +241,8 @@ public class BrightnessMappingStrategyTest { public void testSimpleStrategyIgnoresNewConfiguration() { Resources res = createResources(DISPLAY_LEVELS_INT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); final float[] lux = { 0f, 1f }; final float[] nits = { 0, PowerManager.BRIGHTNESS_ON }; @@ -245,7 +257,8 @@ public class BrightnessMappingStrategyTest { public void testSimpleStrategyIgnoresNullConfiguration() { Resources res = createResources(DISPLAY_LEVELS_INT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); strategy.setBrightnessConfiguration(null); final int n = DISPLAY_LEVELS_INT.length; @@ -261,7 +274,8 @@ public class BrightnessMappingStrategyTest { DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", physical); for (int i = 0; i < LUX_LEVELS.length; i++) { final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1], @@ -279,7 +293,8 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE, LUX_LEVELS, DISPLAY_LEVELS_NITS); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", physical); Spline brightnessToNits = Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS); @@ -297,7 +312,8 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); final float[] lux = {0f, 1f}; final float[] nits = { @@ -323,7 +339,8 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length]; for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) { adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f; @@ -367,7 +384,8 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(DISPLAY_LEVELS_INT); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); } @@ -381,14 +399,16 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); assertNull(strategy); // And make sure we get the same result even if it's monotone but not increasing. lux[idx] = lux[idx + 1]; ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS); - strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNull(strategy); } @@ -402,11 +422,13 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); assertNull(strategy); res = createResources(DISPLAY_LEVELS_INT); - strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNull(strategy); // Extra backlight level @@ -416,7 +438,8 @@ public class BrightnessMappingStrategyTest { res = createResources(backlight); ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, EMPTY_FLOAT_ARRAY); - strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNull(strategy); // Extra nits level @@ -425,7 +448,8 @@ public class BrightnessMappingStrategyTest { res = createResources(EMPTY_INT_ARRAY); ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, nits); - strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNull(strategy); } @@ -433,15 +457,18 @@ public class BrightnessMappingStrategyTest { public void testPhysicalStrategyRequiresNitsMapping() { Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); assertNull(physical); res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); - physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + physical = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNull(physical); res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); - physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); + physical = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT, + mMockDwbc); assertNull(physical); } @@ -450,10 +477,12 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE, LUX_LEVELS, DISPLAY_LEVELS_NITS); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc)); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc)); ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); res = createResources(DISPLAY_LEVELS_INT); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc)); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc)); } @Test @@ -463,7 +492,8 @@ public class BrightnessMappingStrategyTest { // Create an idle mode bms // This will fail if it tries to fetch the wrong configuration. - BrightnessMappingStrategy bms = BrightnessMappingStrategy.createForIdleMode(res, ddc, + BrightnessMappingStrategy bms = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_IDLE, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", bms); @@ -652,7 +682,7 @@ public class BrightnessMappingStrategyTest { DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - mMockDwbc); + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); // Let's start with a validity check: assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -683,7 +713,7 @@ public class BrightnessMappingStrategyTest { DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - mMockDwbc); + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); // Validity check: assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -711,7 +741,7 @@ public class BrightnessMappingStrategyTest { DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - mMockDwbc); + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f); strategy.addUserDataPoint(/* lux= */ 2500, /* brightness= */ 1.0f); assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f); @@ -735,7 +765,7 @@ public class BrightnessMappingStrategyTest { DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, - mMockDwbc); + AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc); // Validity, as per tradition: assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -757,4 +787,14 @@ public class BrightnessMappingStrategyTest { assertEquals(1.0f, strategy.getBrightness(x4), 0.0001f /* tolerance */); assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } + + @Test + public void testGetMode() { + Resources res = createResourcesIdle(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, + AUTO_BRIGHTNESS_MODE_IDLE, + mMockDwbc); + assertEquals(AUTO_BRIGHTNESS_MODE_IDLE, strategy.getMode()); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java index acd9dceb16d5..02bd35a5b17f 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java @@ -18,6 +18,7 @@ package com.android.server.display; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; @@ -28,7 +29,6 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -62,6 +62,7 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.TestableContext; import android.util.FloatProperty; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; @@ -357,7 +358,7 @@ public final class DisplayPowerController2Test { float followerBrightness = 0.4f; float nits = 300; when(mHolder.automaticBrightnessController.convertToNits(leadBrightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(followerBrightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness); listener.onBrightnessChanged(leadBrightness); @@ -372,7 +373,7 @@ public final class DisplayPowerController2Test { float brightness = 0.6f; nits = 600; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -404,7 +405,7 @@ public final class DisplayPowerController2Test { float brightness = 0.3f; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(300f); - when(followerDpc.automaticBrightnessController.convertToFloatScale(anyFloat())) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -466,7 +467,7 @@ public final class DisplayPowerController2Test { float brightness = 0.3f; when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f); - when(followerDpc.automaticBrightnessController.convertToFloatScale(anyFloat())) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -500,7 +501,7 @@ public final class DisplayPowerController2Test { when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) .thenReturn(nits); when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(followerBrightness); mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); @@ -534,7 +535,7 @@ public final class DisplayPowerController2Test { when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) .thenReturn(nits); when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(followerBrightness); mHolder.dpc.updateBrightness(); @@ -592,9 +593,9 @@ public final class DisplayPowerController2Test { float brightness = 0.6f; float nits = 600; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -625,9 +626,9 @@ public final class DisplayPowerController2Test { brightness = 0.7f; nits = 700; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -697,9 +698,9 @@ public final class DisplayPowerController2Test { float brightness = 0.6f; float nits = 600; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(followerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); - when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -1124,7 +1125,7 @@ public final class DisplayPowerController2Test { float newBrightness = 0.4f; when(mHolder.brightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits); - when(mHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(mHolder.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(newBrightness); // New display device setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), @@ -1141,12 +1142,9 @@ public final class DisplayPowerController2Test { @Test public void testShortTermModelPersistsWhenDisplayDeviceChanges() { float lux = 2000; - float brightness = 0.4f; float nits = 500; - when(mHolder.brightnessMappingStrategy.getUserLux()).thenReturn(lux); - when(mHolder.brightnessMappingStrategy.getUserBrightness()).thenReturn(brightness); - when(mHolder.brightnessMappingStrategy.convertToNits(brightness)).thenReturn(nits); - when(mHolder.brightnessMappingStrategy.convertToFloatScale(nits)).thenReturn(brightness); + when(mHolder.automaticBrightnessController.getUserLux()).thenReturn(lux); + when(mHolder.automaticBrightnessController.getUserNits()).thenReturn(nits); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); @@ -1163,7 +1161,7 @@ public final class DisplayPowerController2Test { any(Looper.class), eq(mSensorManagerMock), /* lightSensor= */ any(), - eq(mHolder.brightnessMappingStrategy), + /* brightnessMappingStrategyMap= */ any(SparseArray.class), /* lightSensorWarmUpTime= */ anyInt(), /* brightnessMin= */ anyFloat(), /* brightnessMax= */ anyFloat(), @@ -1182,11 +1180,10 @@ public final class DisplayPowerController2Test { eq(mContext), any(BrightnessRangeController.class), any(BrightnessThrottler.class), - /* idleModeBrightnessMapper= */ isNull(), /* ambientLightHorizonShort= */ anyInt(), /* ambientLightHorizonLong= */ anyInt(), eq(lux), - eq(brightness) + eq(nits) ); } @@ -1219,7 +1216,7 @@ public final class DisplayPowerController2Test { public void testDwbcCallsHappenOnHandler() { mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - mHolder.dpc.setAutomaticScreenBrightnessMode(true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true); // dispatch handler looper @@ -1382,7 +1379,7 @@ public final class DisplayPowerController2Test { BRIGHTNESS_RAMP_DECREASE_MAX); // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); // A second time, when switching to idle mode. @@ -1411,7 +1408,7 @@ public final class DisplayPowerController2Test { BRIGHTNESS_RAMP_DECREASE_MAX); // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); // A second time, when switching to idle mode. @@ -1437,7 +1434,7 @@ public final class DisplayPowerController2Test { mHolder.config, /* isEnabled= */ true); // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); // A second time when switching to idle mode. verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, @@ -1463,7 +1460,7 @@ public final class DisplayPowerController2Test { mHolder.config, /* isEnabled= */ true); // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); @@ -1839,7 +1836,7 @@ public final class DisplayPowerController2Test { AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, - BrightnessMappingStrategy interactiveModeBrightnessMapper, + SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, @@ -1850,15 +1847,13 @@ public final class DisplayPowerController2Test { HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, BrightnessRangeController brightnessRangeController, - BrightnessThrottler brightnessThrottler, - BrightnessMappingStrategy idleModeBrightnessMapper, - int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, - float userBrightness) { + BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userNits) { return mAutomaticBrightnessController; } @Override - BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { return mBrightnessMappingStrategy; diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 50b0e167ef99..64cdac464720 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -18,6 +18,7 @@ package com.android.server.display; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; @@ -28,7 +29,6 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -62,6 +62,7 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.TestableContext; import android.util.FloatProperty; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; @@ -357,7 +358,7 @@ public final class DisplayPowerControllerTest { float followerBrightness = 0.4f; float nits = 300; when(mHolder.automaticBrightnessController.convertToNits(leadBrightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(followerBrightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness); listener.onBrightnessChanged(leadBrightness); @@ -375,7 +376,7 @@ public final class DisplayPowerControllerTest { float brightness = 0.6f; nits = 600; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -408,7 +409,7 @@ public final class DisplayPowerControllerTest { float brightness = 0.3f; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(300f); - when(followerDpc.automaticBrightnessController.convertToFloatScale(anyFloat())) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -472,7 +473,7 @@ public final class DisplayPowerControllerTest { float brightness = 0.3f; when(mHolder.automaticBrightnessController.convertToNits(anyFloat())).thenReturn(-1f); - when(followerDpc.automaticBrightnessController.convertToFloatScale(anyFloat())) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(anyFloat())) .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -507,7 +508,7 @@ public final class DisplayPowerControllerTest { when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) .thenReturn(nits); when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(followerBrightness); mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); @@ -541,7 +542,7 @@ public final class DisplayPowerControllerTest { when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) .thenReturn(nits); when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(followerBrightness); mHolder.dpc.updateBrightness(); @@ -600,9 +601,9 @@ public final class DisplayPowerControllerTest { float brightness = 0.6f; float nits = 600; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -633,9 +634,9 @@ public final class DisplayPowerControllerTest { brightness = 0.7f; nits = 700; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); - when(secondFollowerDpc.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerDpc.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -706,9 +707,9 @@ public final class DisplayPowerControllerTest { float brightness = 0.6f; float nits = 600; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(followerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); - when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerHolder.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); @@ -1056,7 +1057,7 @@ public final class DisplayPowerControllerTest { float newBrightness = 0.4f; when(mHolder.brightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits); - when(mHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(mHolder.automaticBrightnessController.getBrightnessFromNits(nits)) .thenReturn(newBrightness); // New display device setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), @@ -1073,12 +1074,9 @@ public final class DisplayPowerControllerTest { @Test public void testShortTermModelPersistsWhenDisplayDeviceChanges() { float lux = 2000; - float brightness = 0.4f; float nits = 500; - when(mHolder.brightnessMappingStrategy.getUserLux()).thenReturn(lux); - when(mHolder.brightnessMappingStrategy.getUserBrightness()).thenReturn(brightness); - when(mHolder.brightnessMappingStrategy.convertToNits(brightness)).thenReturn(nits); - when(mHolder.brightnessMappingStrategy.convertToFloatScale(nits)).thenReturn(brightness); + when(mHolder.automaticBrightnessController.getUserLux()).thenReturn(lux); + when(mHolder.automaticBrightnessController.getUserNits()).thenReturn(nits); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); @@ -1095,7 +1093,7 @@ public final class DisplayPowerControllerTest { any(Looper.class), eq(mSensorManagerMock), /* lightSensor= */ any(), - eq(mHolder.brightnessMappingStrategy), + /* brightnessMappingStrategyMap= */ any(SparseArray.class), /* lightSensorWarmUpTime= */ anyInt(), /* brightnessMin= */ anyFloat(), /* brightnessMax= */ anyFloat(), @@ -1114,11 +1112,10 @@ public final class DisplayPowerControllerTest { eq(mContext), any(BrightnessRangeController.class), any(BrightnessThrottler.class), - /* idleModeBrightnessMapper= */ isNull(), /* ambientLightHorizonShort= */ anyInt(), /* ambientLightHorizonLong= */ anyInt(), eq(lux), - eq(brightness) + eq(nits) ); } @@ -1151,7 +1148,7 @@ public final class DisplayPowerControllerTest { public void testDwbcCallsHappenOnHandler() { mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); - mHolder.dpc.setAutomaticScreenBrightnessMode(true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true); // dispatch handler looper @@ -1293,7 +1290,7 @@ public final class DisplayPowerControllerTest { BRIGHTNESS_RAMP_DECREASE_MAX); // switch to idle - mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, @@ -1320,7 +1317,7 @@ public final class DisplayPowerControllerTest { BRIGHTNESS_RAMP_DECREASE_MAX); // switch to idle - mHolder.dpc.setAutomaticScreenBrightnessMode(/* idle= */ true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); advanceTime(1); verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, @@ -1347,7 +1344,7 @@ public final class DisplayPowerControllerTest { mHolder.config, /* isEnabled= */ true); // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); // second time when switching to idle screen brightness mode verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, @@ -1372,7 +1369,7 @@ public final class DisplayPowerControllerTest { mHolder.config, /* isEnabled= */ true); // switch to idle mode - mHolder.dpc.setAutomaticScreenBrightnessMode(true); + mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); @@ -1655,7 +1652,7 @@ public final class DisplayPowerControllerTest { AutomaticBrightnessController getAutomaticBrightnessController( AutomaticBrightnessController.Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, - BrightnessMappingStrategy interactiveModeBrightnessMapper, + SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap, int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, @@ -1666,15 +1663,13 @@ public final class DisplayPowerControllerTest { HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, BrightnessRangeController brightnessRangeController, - BrightnessThrottler brightnessThrottler, - BrightnessMappingStrategy idleModeBrightnessMapper, - int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, - float userBrightness) { + BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userNits) { return mAutomaticBrightnessController; } @Override - BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources, DisplayDeviceConfig displayDeviceConfig, DisplayWhiteBalanceController displayWhiteBalanceController) { return mBrightnessMappingStrategy; diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java index 52fa91f5fe0e..2d0c3fdb6352 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java @@ -293,21 +293,21 @@ public final class DisplayBrightnessControllerTest { } @Test - public void testConvertToFloatScale() { + public void testGetBrightnessFromNits() { float brightness = 0.5f; float nits = 300; // ABC is null assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, - mDisplayBrightnessController.convertToFloatScale(nits), /* delta= */ 0); + mDisplayBrightnessController.getBrightnessFromNits(nits), /* delta= */ 0); AutomaticBrightnessController automaticBrightnessController = mock(AutomaticBrightnessController.class); - when(automaticBrightnessController.convertToFloatScale(nits)).thenReturn(brightness); + when(automaticBrightnessController.getBrightnessFromNits(nits)).thenReturn(brightness); mDisplayBrightnessController.setAutomaticBrightnessController( automaticBrightnessController); - assertEquals(brightness, mDisplayBrightnessController.convertToFloatScale(nits), + assertEquals(brightness, mDisplayBrightnessController.getBrightnessFromNits(nits), /* delta= */ 0); } @@ -329,7 +329,7 @@ public final class DisplayBrightnessControllerTest { float brightness = 0.3f; AutomaticBrightnessController automaticBrightnessController = mock(AutomaticBrightnessController.class); - when(automaticBrightnessController.convertToFloatScale(nits)).thenReturn(brightness); + when(automaticBrightnessController.getBrightnessFromNits(nits)).thenReturn(brightness); when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits); mDisplayBrightnessController.setAutomaticBrightnessController( automaticBrightnessController); @@ -340,7 +340,7 @@ public final class DisplayBrightnessControllerTest { // When the nits value is invalid, the brightness is resumed from where it was last set nits = -1; brightness = 0.4f; - when(automaticBrightnessController.convertToFloatScale(nits)).thenReturn(brightness); + when(automaticBrightnessController.getBrightnessFromNits(nits)).thenReturn(brightness); when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits); when(mBrightnessSetting.getBrightness()).thenReturn(brightness); mDisplayBrightnessController.setAutomaticBrightnessController( diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java index b3605ccfc25d..caa08647628e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java @@ -221,6 +221,7 @@ public class AsyncProcessStartTest { r.setStartUid(myUid()); r.setHostingRecord(new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST)); r.makeActive(thread, mAms.mProcessStats); + ProcessRecord.updateProcessRecordNodes(r); doNothing().when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean()); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index e4da2b673efa..cc6fc80d9fa1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -1756,6 +1756,7 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { private ProcessRecord makeProcessRecord(ApplicationInfo info) { final ProcessRecord r = spy(new ProcessRecord(mAms, info, info.processName, info.uid)); r.setPid(mNextPid.incrementAndGet()); + ProcessRecord.updateProcessRecordNodes(r); return r; } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index 0fd424b89837..115a5b0d53f2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -336,6 +336,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { final ProcessRecord r = spy(new ProcessRecord(mAms, ai, processName, ai.uid)); r.mState = spy(r.mState); r.setPid(mNextPid.getAndIncrement()); + ProcessRecord.updateProcessRecordNodes(r); mActiveProcesses.add(r); final IApplicationThread thread; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index f45dd391fbc7..f386c3bf6a15 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -114,7 +114,9 @@ import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** @@ -258,25 +260,40 @@ public class MockingOomAdjusterTests { ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); lru.clear(); Collections.addAll(lru, apps); - for (ProcessRecord app : apps) { - sService.mOomAdjuster.onProcessBeginLocked(app); - } } /** * Run updateOomAdjLocked(). + * - If there is no process specified, run updateOomAdjLocked(int) on existing lru * - If there's only one process, then it calls updateOomAdjLocked(ProcessRecord, int). * - Otherwise, sets the processes to the LRU and run updateOomAdjLocked(int). */ @SuppressWarnings("GuardedBy") private void updateOomAdj(ProcessRecord... apps) { - if (apps.length == 1) { - sService.mOomAdjuster.onProcessBeginLocked(apps[0]); - sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); - } else { - setProcessesToLru(apps); + if (apps.length == 0) { + updateProcessRecordNodes(sService.mProcessList.getLruProcessesLOSP()); sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); - sService.mProcessList.getLruProcessesLOSP().clear(); + } else { + updateProcessRecordNodes(Arrays.asList(apps)); + if (apps.length == 1) { + sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); + } else { + setProcessesToLru(apps); + sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + sService.mProcessList.getLruProcessesLOSP().clear(); + } + } + } + + /** + * Fix up the pointers in the {@link ProcessRecordNode#mApp}: + * because we used the mokito spy objects all over the tests here, but the internal + * pointers in the {@link ProcessRecordNode#mApp} actually point to the real object. + * This needs to be fixed up here. + */ + private void updateProcessRecordNodes(List<ProcessRecord> apps) { + for (ProcessRecord app : apps) { + ProcessRecord.updateProcessRecordNodes(app); } } @@ -613,7 +630,6 @@ public class MockingOomAdjusterTests { s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mOomAdjuster.onProcessBeginLocked(system); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -636,7 +652,6 @@ public class MockingOomAdjusterTests { s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mOomAdjuster.onProcessBeginLocked(system); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -660,7 +675,6 @@ public class MockingOomAdjusterTests { nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mOomAdjuster.onProcessBeginLocked(system); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -679,7 +693,6 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true)); system.mState.setMaxAdj(PERSISTENT_PROC_ADJ); system.mState.setHasTopUi(true); - sService.mOomAdjuster.onProcessBeginLocked(system); // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); @@ -1532,7 +1545,7 @@ public class MockingOomAdjusterTests { client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.updateOomAdjLocked(client2, OOM_ADJ_REASON_NONE); + updateOomAdj(client2); assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState()); assertEquals(PROCESS_STATE_CACHED_EMPTY, client.mState.getSetProcState()); @@ -2018,7 +2031,6 @@ public class MockingOomAdjusterTests { app.setPendingFinishAttach(true); app.mState.setHasForegroundActivities(false); - sService.mOomAdjuster.onProcessBeginLocked(app); sService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); @@ -2035,7 +2047,6 @@ public class MockingOomAdjusterTests { app.mState.setHasForegroundActivities(true); doReturn(app).when(sService).getTopApp(); - sService.mOomAdjuster.onProcessBeginLocked(app); sService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); @@ -2106,7 +2117,7 @@ public class MockingOomAdjusterTests { setServiceMap(s3, MOCKAPP5_UID, cn3); setServiceMap(c2s, MOCKAPP3_UID, cn4); app2UidRecord.setIdle(false); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateOomAdj(); assertProcStates(app1, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); @@ -2470,8 +2481,6 @@ public class MockingOomAdjusterTests { lru.clear(); lru.add(app2); lru.add(app); - sService.mOomAdjuster.onProcessBeginLocked(app2); - sService.mOomAdjuster.onProcessBeginLocked(app); final ComponentName cn = ComponentName.unflattenFromString( MOCKAPP_PACKAGENAME + "/.TestService"); @@ -2506,7 +2515,7 @@ public class MockingOomAdjusterTests { app2.mState.setHasShownUi(false); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services"); assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services"); @@ -2515,7 +2524,7 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(false); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateOomAdj(); assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services"); @@ -2524,7 +2533,7 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2542,7 +2551,7 @@ public class MockingOomAdjusterTests { s.lastActivity = now; app.mServices.startService(s); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateOomAdj(); assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services"); assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2553,7 +2562,7 @@ public class MockingOomAdjusterTests { app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(false); s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateOomAdj(); assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services"); assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2561,7 +2570,7 @@ public class MockingOomAdjusterTests { doReturn(userOther).when(sService.mUserController).getCurrentUserId(); sService.mOomAdjuster.handleUserSwitchedLocked(); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); assertProcStates(app2, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services"); } @@ -2584,7 +2593,7 @@ public class MockingOomAdjusterTests { app.mServices.updateHasAboveClientLocked(); assertFalse(app.mServices.hasAboveClient()); - sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE); + updateOomAdj(app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index 7444403f88c8..ae53e707a7cc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -19,7 +19,6 @@ package com.android.server.pm import android.app.AppOpsManager import android.content.Intent import android.content.pm.SuspendDialogInfo -import android.content.pm.UserPackage import android.os.Binder import android.os.PersistableBundle import com.android.server.testutils.any @@ -42,18 +41,12 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { .thenReturn(AppOpsManager.MODE_DEFAULT) } - companion object { - val doUserPackage = UserPackage.of(TEST_USER_ID, DEVICE_OWNER_PACKAGE) - val platformUserPackage = UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME) - val testUserPackage1 = UserPackage.of(TEST_USER_ID, TEST_PACKAGE_1) - } - @Test fun setPackagesSuspended() { val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() @@ -70,14 +63,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_emptyPackageName() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), null /* packageNames */, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isNull() failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOfNulls(0), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isEmpty() @@ -87,8 +80,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_callerIsNotAllowed() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, - testUserPackage1, TEST_USER_ID, + null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, Binder.getCallingUid(), false /* quarantined */) assertThat(failedNames).asList().hasSize(1) @@ -99,7 +91,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_callerSuspendItself() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).asList().hasSize(1) @@ -110,7 +102,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_nonexistentPackage() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).asList().hasSize(1) @@ -123,7 +115,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE) val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), knownPackages, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */)!! assertThat(failedNames.size).isEqualTo(knownPackages.size) @@ -137,14 +129,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() Mockito.clearInvocations(broadcastHelper) assertThat(failedNames).isEmpty() failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, false /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() @@ -192,7 +184,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_1) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */, - null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -210,22 +202,22 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, appExtras, null /* launcherExtras */, - null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() Mockito.clearInvocations(broadcastHelper) assertThat(failedNames).isEmpty() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) + TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull() assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull() suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspender -> suspender.packageName == DEVICE_OWNER_PACKAGE }, + targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, TEST_USER_ID) testHandler.flush() @@ -251,7 +243,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -266,7 +258,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun isPackageSuspended() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, doUserPackage, + null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -281,13 +273,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) } @Test @@ -298,57 +290,57 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { // Suspend. var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isEmpty() testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) // Suspend by system. failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, platformUserPackage, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, PLATFORM_PACKAGE_NAME, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isEmpty() testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME) // QAS by package1. failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, testUserPackage1, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid, true /* quarantined */) assertThat(failedNames).isEmpty() testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(testUserPackage1) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(TEST_PACKAGE_1) // Un-QAS by package1. suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspendingPackage -> suspendingPackage == testUserPackage1 }, + targetPackages, { suspendingPackage -> suspendingPackage == TEST_PACKAGE_1 }, TEST_USER_ID) testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME) // Un-suspend by system. suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspender -> suspender.packageName == PLATFORM_PACKAGE_NAME }, + targetPackages, { suspendingPackage -> suspendingPackage == PLATFORM_PACKAGE_NAME }, TEST_USER_ID) testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) // Unsuspend. suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspendingPackage -> suspendingPackage == doUserPackage }, + targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, TEST_USER_ID) testHandler.flush() @@ -362,13 +354,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { .setTitle(TEST_PACKAGE_1).build() var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, dialogInfo, doUserPackage, TEST_USER_ID, + null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() val result = suspendPackageHelper.getSuspendedDialogInfo(pms.snapshotComputer(), - TEST_PACKAGE_1, doUserPackage, TEST_USER_ID, deviceOwnerUid)!! + TEST_PACKAGE_1, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!! assertThat(result.title).isEqualTo(TEST_PACKAGE_1) } diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index b2854ceb1017..ef15f60101d4 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -868,6 +868,7 @@ public class CompatConfigTest { + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />" + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />" + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />" + + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />" + "</config>"; File dir = createTempDir(); @@ -885,32 +886,12 @@ public class CompatConfigTest { ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse(); assertThat(compatConfig.isChangeEnabled(1236L, ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); - } - - @Test - public void testReadApexConfig() throws IOException { - String configXml = "<config>" - + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />" - + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />" - + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />" - + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />" - + "</config>"; - - File dir = createTempDir(); - writeToFile(dir, "platform_compat_config.xml", configXml); - CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); - compatConfig.forceNonDebuggableFinalForTest(false); + assertThat(compatConfig.isChangeEnabled(1237L, + ApplicationInfoBuilder.create().withTargetSdk(31).build())).isFalse(); - compatConfig.initConfigFromLib(dir); + // Force the platform sdk version to be same as enabled target sdk + when(mBuildClassifier.platformTargetSdk()).thenReturn(31); - assertThat(compatConfig.isChangeEnabled(1234L, - ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse(); - assertThat(compatConfig.isChangeEnabled(1234L, - ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); - assertThat(compatConfig.isChangeEnabled(1235L, - ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse(); - assertThat(compatConfig.isChangeEnabled(1236L, - ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); assertThat(compatConfig.isChangeEnabled(1237L, ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index 1dd64ffa5dde..943a9c4759c4 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -18,6 +18,7 @@ package com.android.server.devicepolicy; import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -221,21 +222,21 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R); // Pretend some packages are suspended. - when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages( - USER_SYSTEM)).thenReturn(true); + when(getServices().packageManagerInternal.isSuspendingAnyPackages( + PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true); final DevicePolicyManagerServiceTestable dpms = bootDpmsUp(); verify(getServices().packageManagerInternal, never()) - .unsuspendAdminSuspendedPackages(USER_SYSTEM); + .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM); sendBroadcastWithUser(dpms, Intent.ACTION_USER_STARTED, USER_SYSTEM); // Verify that actual package suspension state is not modified after user start verify(getServices().packageManagerInternal, never()) - .unsuspendAdminSuspendedPackages(USER_SYSTEM); + .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM); verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser( - any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt(), anyInt()); + any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt()); final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext); poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); @@ -254,14 +255,14 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q); // Pretend some packages are suspended. - when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages( - USER_SYSTEM)).thenReturn(true); + when(getServices().packageManagerInternal.isSuspendingAnyPackages( + PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true); final DevicePolicyManagerServiceTestable dpms = bootDpmsUp(); // Verify that apps get unsuspended. verify(getServices().packageManagerInternal) - .unsuspendAdminSuspendedPackages(USER_SYSTEM); + .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM); final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext); poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 24704034ae0c..f4dac2c10d0f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -63,6 +63,7 @@ import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH; import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE; import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION; import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.testutils.TestUtils.assertExpectException; import static com.google.common.truth.Truth.assertThat; @@ -5079,7 +5080,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().iwindowManager).refreshScreenCaptureDisabled(); // Unsuspend personal apps verify(getServices().packageManagerInternal) - .unsuspendAdminSuspendedPackages(UserHandle.USER_SYSTEM); + .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM); verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false); @@ -7534,7 +7535,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); // Verify that the apps are NOT unsuspeded. verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser( - any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt(), anyInt()); + any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt()); // Verify that DPC is invoked to check policy compliance. verify(mContext.spiedContext).startActivityAsUser( MockUtils.checkIntentAction(ACTION_CHECK_POLICY_COMPLIANCE), diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 708ee352a8c9..99fa30c588db 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -1706,6 +1706,15 @@ public class HdmiControlServiceTest { verify(mHdmiControlServiceSpy, times(1)).startArcAction(eq(false), any()); } + @Test + public void onHotplugIn_invalidPortId_noAddressAllocation() { + mHdmiControlServiceSpy.onHotplug(-1, true); + mTestLooper.dispatchAll(); + + verify(mHdmiControlServiceSpy, times(0)) + .allocateLogicalAddress(any(), eq(INITIATED_BY_HOTPLUG)); + } + protected static class MockPlaybackDevice extends HdmiCecLocalDevicePlayback { private boolean mCanGoToStandby; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 48c00a86fd17..56c75b52cf18 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -75,6 +75,7 @@ import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; @@ -474,7 +475,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; TestableFlagResolver mTestFlagResolver = new TestableFlagResolver(); - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @Mock @@ -640,6 +641,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { }); // TODO (b/291907312): remove feature flag + // NOTE: Prefer using the @EnableFlag annotation where possible. Do not add any android.app + // flags here. mSetFlagsRule.disableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER, Flags.FLAG_POLITE_NOTIFICATIONS); initNMS(); @@ -6316,15 +6319,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon3.getUri())); } + private PendingIntent getPendingIntentWithUri(Uri uri) { + return PendingIntent.getActivity(mContext, 0, + new Intent("action", uri), + PendingIntent.FLAG_IMMUTABLE); + } + @Test - public void testVisitUris_callStyle() { + @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) + public void testVisitUris_callStyle_ongoingCall() { Icon personIcon = Icon.createWithContentUri("content://media/person"); Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); Person callingPerson = new Person.Builder().setName("Someone") .setIcon(personIcon) .build(); - PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(), - PendingIntent.FLAG_IMMUTABLE); + Uri hangUpUri = Uri.parse("content://intent/hangup"); + PendingIntent hangUpIntent = getPendingIntentWithUri(hangUpUri); Notification n = new Notification.Builder(mContext, "a") .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent) .setVerificationIcon(verificationIcon)) @@ -6337,6 +6347,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon.getUri())); verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); + verify(visitor, times(1)).accept(eq(hangUpUri)); + } + + @Test + @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) + public void testVisitUris_callStyle_incomingCall() { + Icon personIcon = Icon.createWithContentUri("content://media/person"); + Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); + Person callingPerson = new Person.Builder().setName("Someone") + .setIcon(personIcon) + .build(); + Uri answerUri = Uri.parse("content://intent/answer"); + PendingIntent answerIntent = getPendingIntentWithUri(answerUri); + Uri declineUri = Uri.parse("content://intent/decline"); + PendingIntent declineIntent = getPendingIntentWithUri(declineUri); + Notification n = new Notification.Builder(mContext, "a") + .setStyle(Notification.CallStyle.forIncomingCall(callingPerson, declineIntent, + answerIntent) + .setVerificationIcon(verificationIcon)) + .setContentTitle("Calling...") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .build(); + + Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); + n.visitUris(visitor); + + verify(visitor, times(1)).accept(eq(personIcon.getUri())); + verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); + verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData())); + verify(visitor, times(1)).accept(eq(declineUri)); } @Test @@ -6385,23 +6425,80 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) public void testVisitUris_wearableExtender() { Icon actionIcon = Icon.createWithContentUri("content://media/action"); Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction"); - PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(), - PendingIntent.FLAG_IMMUTABLE); + Uri displayIntentUri = Uri.parse("content://intent/display"); + PendingIntent displayIntent = getPendingIntentWithUri(displayIntentUri); + Uri actionIntentUri = Uri.parse("content://intent/action"); + PendingIntent actionIntent = getPendingIntentWithUri(actionIntentUri); + Uri wearActionIntentUri = Uri.parse("content://intent/wear"); + PendingIntent wearActionIntent = getPendingIntentWithUri(wearActionIntentUri); Notification n = new Notification.Builder(mContext, "a") .setSmallIcon(android.R.drawable.sym_def_app_icon) - .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build()) - .extend(new Notification.WearableExtender().addAction( - new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build())) + .addAction( + new Notification.Action.Builder(actionIcon, "Hey!", actionIntent).build()) + .extend(new Notification.WearableExtender() + .setDisplayIntent(displayIntent) + .addAction(new Notification.Action.Builder(wearActionIcon, "Wear!", + wearActionIntent) + .build())) .build(); Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); n.visitUris(visitor); verify(visitor).accept(eq(actionIcon.getUri())); + verify(visitor, times(1)).accept(eq(actionIntentUri)); verify(visitor).accept(eq(wearActionIcon.getUri())); + verify(visitor, times(1)).accept(eq(wearActionIntentUri)); + } + + @Test + @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) + public void testVisitUris_tvExtender() { + Uri contentIntentUri = Uri.parse("content://intent/content"); + PendingIntent contentIntent = getPendingIntentWithUri(contentIntentUri); + Uri deleteIntentUri = Uri.parse("content://intent/delete"); + PendingIntent deleteIntent = getPendingIntentWithUri(deleteIntentUri); + Notification n = new Notification.Builder(mContext, "a") + .extend( + new Notification.TvExtender() + .setContentIntent(contentIntent) + .setDeleteIntent(deleteIntent)) + .build(); + + Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); + n.visitUris(visitor); + + verify(visitor, times(1)).accept(eq(contentIntentUri)); + verify(visitor, times(1)).accept(eq(deleteIntentUri)); + } + + @Test + @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) + public void testVisitUris_carExtender() { + final String testParticipant = "testParticipant"; + Uri readPendingIntentUri = Uri.parse("content://intent/read"); + PendingIntent readPendingIntent = getPendingIntentWithUri(readPendingIntentUri); + Uri replyPendingIntentUri = Uri.parse("content://intent/reply"); + PendingIntent replyPendingIntent = getPendingIntentWithUri(replyPendingIntentUri); + final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build(); + + Notification n = new Notification.Builder(mContext, "a") + .extend(new Notification.CarExtender().setUnreadConversation( + new Notification.CarExtender.Builder(testParticipant) + .setReadPendingIntent(readPendingIntent) + .setReplyAction(replyPendingIntent, testRemoteInput) + .build())) + .build(); + + Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); + n.visitUris(visitor); + + verify(visitor, times(1)).accept(eq(readPendingIntentUri)); + verify(visitor, times(1)).accept(eq(replyPendingIntentUri)); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java index 44dbe385a144..ea948ca0e28b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java @@ -146,6 +146,10 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { .put(Notification.Action.Builder.class, "extend") // Overwrites icon supplied to constructor. .put(Notification.BubbleMetadata.Builder.class, "setIcon") + // Overwrites intent supplied to constructor. + .put(Notification.BubbleMetadata.Builder.class, "setIntent") + // Overwrites intent supplied to constructor. + .put(Notification.BubbleMetadata.Builder.class, "setDeleteIntent") // Discards previously-added actions. .put(RemoteViews.class, "mergeRemoteViews") .build(); @@ -680,14 +684,14 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { } if (clazz == Intent.class) { - // TODO(b/281044385): Are Intent Uris (new Intent(String,Uri)) relevant? - return new Intent("action"); + return new Intent("action", generateUri(where.plus(Intent.class))); } if (clazz == PendingIntent.class) { - // PendingIntent can have an Intent with a Uri but those are inaccessible and - // not inspected. - return PendingIntent.getActivity(mContext, 0, new Intent("action"), + // PendingIntent can have an Intent with a Uri. + Uri intentUri = generateUri(where.plus(PendingIntent.class)); + return PendingIntent.getActivity(mContext, 0, + new Intent("action", intentUri), PendingIntent.FLAG_IMMUTABLE); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 670f9f697a5c..526201f9c1c6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -49,7 +49,6 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; -import android.content.pm.UserPackage; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -196,7 +195,7 @@ public class ActivityStartInterceptorTest { mAInfo.applicationInfo.flags = FLAG_SUSPENDED; when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID)) - .thenReturn(UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME)); + .thenReturn(PLATFORM_PACKAGE_NAME); // THEN calling intercept returns true assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); @@ -228,10 +227,9 @@ public class ActivityStartInterceptorTest { .setMessage("Test Message") .setIcon(0x11110001) .build(); - UserPackage suspender = UserPackage.of(TEST_USER_ID, suspendingPackage); when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID)) - .thenReturn(suspender); - when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspender, + .thenReturn(suspendingPackage); + when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage, TEST_USER_ID)).thenReturn(dialogInfo); return dialogInfo; } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index b167f1bdfe20..d05eb5cd2d4c 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1349,6 +1349,24 @@ public class TelecomManager { } /** + * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone + * calls. The returned list includes those accounts which have been explicitly enabled by + * the user or other users visible to the user. + * + * @see #EXTRA_PHONE_ACCOUNT_HANDLE + * @return A list of {@code PhoneAccountHandle} objects. + * + * @throws IllegalStateException if telecom service is null. + */ + @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_WORK_PROFILE_API_SPLIT) + @RequiresPermission(allOf = {android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.INTERACT_ACROSS_PROFILES}) + public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles() { + return getCallCapablePhoneAccountsAcrossProfiles(false); + } + + + /** * Returns a list of {@link PhoneAccountHandle}s for all self-managed * {@link ConnectionService}s owned by the calling {@link UserHandle}. * <p> @@ -1423,7 +1441,7 @@ public class TelecomManager { if (service != null) { try { return service.getCallCapablePhoneAccounts(includeDisabledAccounts, - mContext.getOpPackageName(), mContext.getAttributionTag()).getList(); + mContext.getOpPackageName(), mContext.getAttributionTag(), false).getList(); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" + includeDisabledAccounts + ")", e); @@ -1433,6 +1451,37 @@ public class TelecomManager { } /** + * Returns a list of {@link PhoneAccountHandle}s visible to current user including those which + * have not been enabled by the user. + * + * @param includeDisabledAccounts When {@code true}, disabled phone accounts will be included, + * when {@code false}, only enabled phone accounts will be + * included. + * @return A list of {@code PhoneAccountHandle} objects. + * + * @throws IllegalStateException if telecom service is null. + * @hide + */ + @SystemApi + @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_WORK_PROFILE_API_SPLIT) + @RequiresPermission(allOf = {android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.INTERACT_ACROSS_PROFILES}) + public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccountsAcrossProfiles( + boolean includeDisabledAccounts) { + ITelecomService service = getTelecomService(); + if (service == null) { + throw new IllegalStateException("telecom service is null."); + } + + try { + return service.getCallCapablePhoneAccounts(includeDisabledAccounts, + mContext.getOpPackageName(), mContext.getAttributionTag(), true).getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * Returns a list of all {@link PhoneAccount}s registered for the calling package. * * @deprecated Use {@link #getSelfManagedPhoneAccounts()} instead to get only self-managed diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 53154e6063b2..f1bfd227298c 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -61,7 +61,8 @@ interface ITelecomService { * @see TelecomServiceImpl#getCallCapablePhoneAccounts */ ParceledListSlice<PhoneAccountHandle> getCallCapablePhoneAccounts( - boolean includeDisabledAccounts, String callingPackage, String callingFeatureId); + boolean includeDisabledAccounts, String callingPackage, + String callingFeatureId, boolean acrossProfiles); /** * @see TelecomServiceImpl#getSelfManagedPhoneAccounts diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 7cb2cc398c46..bcd99295b605 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9577,14 +9577,20 @@ public class CarrierConfigManager { "satellite_attach_supported_bool"; /** - * The carrier-enabled satellite connection hysteresis time in seconds to determine whether to - * recommend Dialer to prompt users to use satellite emergency messaging. + * The carrier-enabled satellite connection hysteresis time in seconds for which the device + * continues in satellite mode after it loses the connection with the satellite network. * <p> - * A timer is started when there is an ongoing emergency call, and the IMS is not registered, - * and cellular service is not available, and the device was connected to a satellite network - * within this time in the past. When the timer expires, Telephony will send the event + * If the device is in satellite mode, the following actions will be taken by the device: + * <ul> + * <li>System UI will continue showing the satellite icon.</li> + * <li>When there is an ongoing emergency call, and the IMS is not registered, and cellular + * service is not available, and the device is in satellite mode, a timer with a duration + * defined by the overlay config + * {@code config_emergency_call_wait_for_connection_timeout_millis} will be started. When the + * timer expires, Telephony will send the event * {@link TelephonyManager#EVENT_DISPLAY_EMERGENCY_MESSAGE} to Dialer, which will then prompt - * users to switch to using satellite emergency messaging. + * users to switch to using satellite emergency messaging.</li> + * </ul> * <p> * The default value is 300 seconds. */ diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 60cc9c7f73cf..df349f89fbf8 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -3556,4 +3556,29 @@ public final class SmsManager { } return smscUri; } + + /** + * Gets the message size of a WAP from the cache. + * + * @param locationUrl the location to use as a key for looking up the size in the cache. + * The locationUrl may or may not have the transactionId appended to the url. + * + * @return long representing the message size + * @throws java.util.NoSuchElementException if the WAP push doesn't exist in the cache + * @throws IllegalArgumentException if the locationUrl is empty + * + * @hide + */ + public long getWapMessageSize(@NonNull String locationUrl) { + try { + ISms iSms = getISmsService(); + if (iSms != null) { + return iSms.getWapMessageSize(locationUrl); + } else { + throw new RuntimeException("Could not acquire ISms service."); + } + } catch (RemoteException ex) { + throw new RuntimeException(ex); + } + } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 55a3da5ad89d..f8166e58cd2f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1207,7 +1207,8 @@ public class TelephonyManager { * The dialer app receives this event via * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}. * <p> - * The {@link Bundle} parameter is expected to include the following extras: + * The {@link Bundle} parameter is guaranteed to include the following extras if the below + * conditions are met: * <ul> * <li>{@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE} - the recommending handover * type.</li> diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 2172d7de0dd7..71bb329a7281 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresFeature; @@ -45,6 +46,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -447,6 +449,114 @@ public class ImsMmTelManager implements RegistrationManager { } /** + * Registers a {@link RegistrationCallback} with the system, which will provide IMS emergency + * registration updates for the subscription specified in + * {@link ImsManager#getImsMmTelManager(int)}. Use + * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed + * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. + * + * When the callback is registered, it will initiate the callback c to be called with the + * current emergency registration state. + * Emergency registration callback is available when there is valid SIM card. + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * + * @param executor The executor the callback events should be run on. + * @param c The {@link RegistrationCallback} to be added. + * @see #unregisterImsEmergencyRegistrationCallback + * @throws ImsException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed + * reason. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public void registerImsEmergencyRegistrationCallback( + @NonNull @CallbackExecutor Executor executor, + @NonNull RegistrationManager.RegistrationCallback c) throws ImsException { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + c.setExecutor(executor); + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + iTelephony.registerImsEmergencyRegistrationCallback(mSubId, c.getBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } catch (IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Removes an existing {@link RegistrationCallback} for Emergency IMS registration. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. If this method is called for an + * inactive subscription, it will result in a no-op. + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * + * @param c The {@link RegistrationCallback} to be removed. + * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener + * @see #registerImsEmergencyRegistrationCallback(Executor, + * RegistrationManager.RegistrationCallback) + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public void unregisterImsEmergencyRegistrationCallback( + @NonNull RegistrationManager.RegistrationCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + Log.w("ImsMmTelManager", "Could not find Telephony Service."); + return; + } + + try { + iTelephony.unregisterImsEmergencyRegistrationCallback(mSubId, c.getBinder()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * {@inheritDoc} * @hide */ diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java index 50c438c7678e..f548dbada3cb 100644 --- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java @@ -16,6 +16,7 @@ package android.telephony.ims; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,6 +27,8 @@ import android.telephony.AccessNetworkConstants; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.ArraySet; +import com.android.internal.telephony.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -46,12 +49,36 @@ public final class ImsRegistrationAttributes implements Parcelable { * */ public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1 << 0; + /** + * Attribute to specify if ims registration is of type normal or emergency. + * <p> + * For emergency registration bit will be set. + * For normal registration bit will not be set. + * This flag is only applicable when listening to emergency IMS registration state updates + * via the ImsMmTelManager#registerImsEmergencyRegistrationCallback API + * </p> + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public static final int ATTR_REGISTRATION_TYPE_EMERGENCY = 1 << 1; + /** + * Attribute to specify if virtual registration is required. + * <p> + * If emergency registration is not required for making emergency call, in such cases + * bit will be set and callback will represent virtual registration status update. + * This flag is only applicable when listening to emergency IMS registration state updates + * via the ImsMmTelManager#registerImsEmergencyRegistrationCallback API + * </p> + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public static final int ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL = 1 << 2; /** @hide */ // Defines the underlying radio technology type that we have registered for IMS over. @IntDef(prefix = "ATTR_", value = { ATTR_EPDG_OVER_CELL_INTERNET, + ATTR_REGISTRATION_TYPE_EMERGENCY, + ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL, }, flag = true) @Retention(RetentionPolicy.SOURCE) @@ -67,6 +94,8 @@ public final class ImsRegistrationAttributes implements Parcelable { private Set<String> mFeatureTags = Collections.emptySet(); private @Nullable SipDetails mSipDetails; + private @ImsAttributeFlag int mAttributeFlags; + /** * Build a new instance of {@link ImsRegistrationAttributes}. * @@ -74,6 +103,9 @@ public final class ImsRegistrationAttributes implements Parcelable { */ public Builder(@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) { mRegistrationTech = registrationTech; + if (registrationTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { + mAttributeFlags |= ATTR_EPDG_OVER_CELL_INTERNET; + } } /** @@ -110,25 +142,32 @@ public final class ImsRegistrationAttributes implements Parcelable { } /** + * Set the attribute flag ATTR_REGISTRATION_TYPE_EMERGENCY. + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public @NonNull Builder setFlagRegistrationTypeEmergency() { + mAttributeFlags |= ATTR_REGISTRATION_TYPE_EMERGENCY; + return this; + } + + /** + * Set the attribute flag ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL. + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public @NonNull Builder setFlagVirtualRegistrationForEmergencyCall() { + mAttributeFlags |= ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL; + return this; + } + + /** * @return A new instance created from this builder. */ public @NonNull ImsRegistrationAttributes build() { return new ImsRegistrationAttributes(mRegistrationTech, RegistrationManager.getAccessType(mRegistrationTech), - getAttributeFlags(mRegistrationTech), + mAttributeFlags, mFeatureTags, mSipDetails); } - - /** - * @return attribute flags from the registration technology. - */ - private static int getAttributeFlags(int imsRadioTech) { - int attributes = 0; - if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { - attributes |= ATTR_EPDG_OVER_CELL_INTERNET; - } - return attributes; - } } private final int mRegistrationTech; diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl index 219c9c88d825..2a6b8f292d0b 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl @@ -28,6 +28,8 @@ interface IImsRegistration { int getRegistrationTechnology(); oneway void addRegistrationCallback(IImsRegistrationCallback c); oneway void removeRegistrationCallback(IImsRegistrationCallback c); + oneway void addEmergencyRegistrationCallback(IImsRegistrationCallback c); + oneway void removeEmergencyRegistrationCallback(IImsRegistrationCallback c); oneway void triggerFullNetworkRegistration(int sipCode, String sipReason); oneway void triggerUpdateSipDelegateRegistration(); oneway void triggerSipDelegateDeregistration(); diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index ff378ba02856..99c26b01bf01 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -16,6 +16,7 @@ package android.telephony.ims.stub; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -31,6 +32,7 @@ import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.util.Log; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.util.RemoteCallbackListExt; import com.android.internal.telephony.util.TelephonyUtils; import com.android.internal.util.ArrayUtils; @@ -56,7 +58,6 @@ import java.util.function.Supplier; public class ImsRegistrationImplBase { private static final String LOG_TAG = "ImsRegistrationImplBase"; - /** * @hide */ @@ -233,6 +234,31 @@ public class ImsRegistrationImplBase { } @Override + public void addEmergencyRegistrationCallback(IImsRegistrationCallback c) + throws RemoteException { + AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); + executeMethodAsync(() -> { + try { + ImsRegistrationImplBase.this.addEmergencyRegistrationCallback(c); + } catch (RemoteException e) { + exceptionRef.set(e); + } + }, "addEmergencyRegistrationCallback"); + + if (exceptionRef.get() != null) { + throw exceptionRef.get(); + } + } + + @Override + public void removeEmergencyRegistrationCallback(IImsRegistrationCallback c) + throws RemoteException { + executeMethodAsync(() -> + ImsRegistrationImplBase.this.removeEmergencyRegistrationCallback(c), + "removeEmergencyRegistrationCallback"); + } + + @Override public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c), "removeRegistrationCallback"); @@ -302,16 +328,23 @@ public class ImsRegistrationImplBase { private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks = new RemoteCallbackListExt<>(); + private final RemoteCallbackListExt<IImsRegistrationCallback> mEmergencyCallbacks = + new RemoteCallbackListExt<>(); private final Object mLock = new Object(); // Locked on mLock private ImsRegistrationAttributes mRegistrationAttributes; + private ImsRegistrationAttributes mEmergencyRegistrationAttributes; // Locked on mLock private int mRegistrationState = REGISTRATION_STATE_UNKNOWN; + private int mEmergencyRegistrationState = REGISTRATION_STATE_UNKNOWN; // Locked on mLock, create unspecified disconnect cause. private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo(); + private ImsReasonInfo mEmergencyLastDisconnectCause = new ImsReasonInfo(); // Locked on mLock private int mLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE; + private int mEmergencyLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE; private int mLastDisconnectRadioTech = REGISTRATION_TECH_NONE; + private int mEmergencyLastDisconnectRadioTech = REGISTRATION_TECH_NONE; // We hold onto the uris each time they change so that we can send it to a callback when its // first added. @@ -331,7 +364,7 @@ public class ImsRegistrationImplBase { // do not want to risk introducing a deadlock by using the same mCallbacks Object to // synchronize on outgoing and incoming operations. mCallbacks.register(c); - updateNewCallbackWithState(c); + updateNewCallbackWithState(c, false); } private void removeRegistrationCallback(IImsRegistrationCallback c) { @@ -342,6 +375,16 @@ public class ImsRegistrationImplBase { mCallbacks.unregister(c); } + private void addEmergencyRegistrationCallback(IImsRegistrationCallback c) + throws RemoteException { + mEmergencyCallbacks.register(c); + updateNewCallbackWithState(c, true); + } + + private void removeEmergencyRegistrationCallback(IImsRegistrationCallback c) { + mEmergencyCallbacks.unregister(c); + } + /** * Called by the framework to request that the ImsService perform the network registration * of all SIP delegates associated with this ImsService. @@ -428,14 +471,19 @@ public class ImsRegistrationImplBase { */ @SystemApi public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) { - updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); + boolean isEmergency = isEmergency(attributes); + if (isEmergency) { + updateToEmergencyState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); + } else { + updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); + } broadcastToCallbacksLocked((c) -> { try { c.onRegistered(attributes); } catch (RemoteException e) { Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback."); } - }); + }, isEmergency); } /** @@ -457,14 +505,19 @@ public class ImsRegistrationImplBase { */ @SystemApi public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) { - updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); + boolean isEmergency = isEmergency(attributes); + if (isEmergency) { + updateToEmergencyState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); + } else { + updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); + } broadcastToCallbacksLocked((c) -> { try { c.onRegistering(attributes); } catch (RemoteException e) { Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback."); } - }); + }, isEmergency); } /** @@ -513,16 +566,57 @@ public class ImsRegistrationImplBase { public final void onDeregistered(@Nullable ImsReasonInfo info, @RegistrationManager.SuggestedAction int suggestedAction, @ImsRegistrationTech int imsRadioTech) { - updateToDisconnectedState(info, suggestedAction, imsRadioTech); + // Impl to keep backwards compatibility with old implementations + ImsRegistrationAttributes attributes = mRegistrationAttributes != null + ? new ImsRegistrationAttributes(imsRadioTech, + mRegistrationAttributes.getTransportType(), + mRegistrationAttributes.getAttributeFlags(), + mRegistrationAttributes.getFeatureTags()) : + new ImsRegistrationAttributes.Builder(imsRadioTech).build(); + onDeregistered(info, suggestedAction, attributes); + } + + /** + * Notify the framework that the device is disconnected from the IMS network. + * <p> + * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo,int)}, you should ensure that any + * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent + * to the framework. For example, + * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} + * and + * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} + * may be set to unavailable to ensure the framework knows these services are no longer + * available due to de-registration. If you do not report capability changes impacted by + * de-registration, the framework will not know which features are no longer available as a + * result. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + * @param suggestedAction the expected behavior of radio protocol stack. + * @param attributes The attributes associated with the IMS registration + * @hide This API is not part of the Android public SDK API + */ + @SystemApi + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public final void onDeregistered(@Nullable ImsReasonInfo info, + @RegistrationManager.SuggestedAction int suggestedAction, + @NonNull ImsRegistrationAttributes attributes) { + boolean isEmergency = isEmergency(attributes); + int imsRadioTech = attributes.getRegistrationTechnology(); + if (isEmergency) { + updateToDisconnectedEmergencyState(info, suggestedAction, imsRadioTech); + } else { + updateToDisconnectedState(info, suggestedAction, imsRadioTech); + } // ImsReasonInfo should never be null. final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); + broadcastToCallbacksLocked((c) -> { try { c.onDeregistered(reasonInfo, suggestedAction, imsRadioTech); } catch (RemoteException e) { Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); } - }); + }, isEmergency); } /** @@ -568,6 +662,7 @@ public class ImsRegistrationImplBase { * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. * @param suggestedAction the expected behavior of radio protocol stack. + * @param imsRadioTech the network type on which IMS registration has failed. * @param details the {@link SipDetails} related to disconnected Ims registration * @hide This API is not part of the Android public SDK API */ @@ -584,7 +679,7 @@ public class ImsRegistrationImplBase { } catch (RemoteException e) { Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); } - }); + }, false); } /** @@ -599,14 +694,38 @@ public class ImsRegistrationImplBase { @SystemApi public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) { + ImsRegistrationAttributes attributes = mRegistrationAttributes != null + ? new ImsRegistrationAttributes(imsRadioTech, + mRegistrationAttributes.getTransportType(), + mRegistrationAttributes.getAttributeFlags(), + mRegistrationAttributes.getFeatureTags()) : + new ImsRegistrationAttributes.Builder(imsRadioTech).build(); + onTechnologyChangeFailed(info, attributes); + } + + /** + * Notify the framework that the handover from the current radio technology to the technology + * defined in {@code imsRadioTech} has failed. + * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and + * {@link #REGISTRATION_TECH_CROSS_SIM}. + * @param info The {@link ImsReasonInfo} for the failure to change technology. + * @param attributes The attributes associated with the IMS registration + * @hide This API is not part of the Android public SDK API + */ + @SystemApi + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public final void onTechnologyChangeFailed(@Nullable ImsReasonInfo info, + @NonNull ImsRegistrationAttributes attributes) { + boolean isEmergency = isEmergency(attributes); + int imsRadioTech = attributes.getRegistrationTechnology(); final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); - broadcastToCallbacksLocked((c) -> { + broadcastToCallbacksLocked(c -> { try { c.onTechnologyChangeFailed(imsRadioTech, reasonInfo); } catch (RemoteException e) { Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback."); } - }); + }, isEmergency); } /** @@ -623,19 +742,35 @@ public class ImsRegistrationImplBase { mUris = ArrayUtils.cloneOrNull(uris); mUrisSet = true; } - broadcastToCallbacksLocked((c) -> onSubscriberAssociatedUriChanged(c, uris)); + broadcastToCallbacksLocked((c) -> onSubscriberAssociatedUriChanged(c, uris), false); + } + + private boolean isEmergency(ImsRegistrationAttributes attributes) { + if (attributes == null) { + return false; + } else { + return (attributes.getAttributeFlags() + & ImsRegistrationAttributes.ATTR_REGISTRATION_TYPE_EMERGENCY) != 0; + } } /** - * Broadcast the specified operation in a synchronized manner so that multiple threads do not + * Broadcast the specified operation in ta synchronized manner so that multiple threads do not * try to call broadcast at the same time, which will generate an error. * @param c The Consumer lambda method containing the callback to call. */ - private void broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c) { + private void broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c, + boolean isEmergency) { // One broadcast can happen at a time, so synchronize threads so only one // beginBroadcast/endBroadcast happens at a time. - synchronized (mCallbacks) { - mCallbacks.broadcastAction(c); + if (isEmergency) { + synchronized (mEmergencyCallbacks) { + mEmergencyCallbacks.broadcastAction(c); + } + } else { + synchronized (mCallbacks) { + mCallbacks.broadcastAction(c); + } } } @@ -657,6 +792,16 @@ public class ImsRegistrationImplBase { } } + private void updateToEmergencyState(ImsRegistrationAttributes attributes, int newState) { + synchronized (mLock) { + mEmergencyRegistrationAttributes = attributes; + mEmergencyRegistrationState = newState; + mEmergencyLastDisconnectCause = null; + mEmergencyLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE; + mEmergencyLastDisconnectRadioTech = REGISTRATION_TECH_NONE; + } + } + private void updateToDisconnectedState(ImsReasonInfo info, @RegistrationManager.SuggestedAction int suggestedAction, @ImsRegistrationTech int imsRadioTech) { @@ -678,11 +823,33 @@ public class ImsRegistrationImplBase { } } + private void updateToDisconnectedEmergencyState(ImsReasonInfo info, + @RegistrationManager.SuggestedAction int suggestedAction, + @ImsRegistrationTech int imsRadioTech) { + synchronized (mLock) { + //We don't want to send this info over if we are disconnected + mUrisSet = false; + mUris = null; + + updateToEmergencyState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE) + .build(), + RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); + if (info != null) { + mEmergencyLastDisconnectCause = info; + mEmergencyLastDisconnectSuggestedAction = suggestedAction; + mEmergencyLastDisconnectRadioTech = imsRadioTech; + } else { + Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); + mEmergencyLastDisconnectCause = new ImsReasonInfo(); + } + } + } + /** * @param c the newly registered callback that will be updated with the current registration * state. */ - private void updateNewCallbackWithState(IImsRegistrationCallback c) + private void updateNewCallbackWithState(IImsRegistrationCallback c, boolean isEmergencyCallback) throws RemoteException { int state; ImsRegistrationAttributes attributes; @@ -692,11 +859,15 @@ public class ImsRegistrationImplBase { boolean urisSet; Uri[] uris; synchronized (mLock) { - state = mRegistrationState; - attributes = mRegistrationAttributes; - disconnectInfo = mLastDisconnectCause; - suggestedAction = mLastDisconnectSuggestedAction; - imsDisconnectRadioTech = mLastDisconnectRadioTech; + state = isEmergencyCallback ? mEmergencyRegistrationState : mRegistrationState; + attributes = isEmergencyCallback ? mEmergencyRegistrationAttributes : + mRegistrationAttributes; + disconnectInfo = isEmergencyCallback ? mEmergencyLastDisconnectCause : + mLastDisconnectCause; + suggestedAction = isEmergencyCallback ? mEmergencyLastDisconnectSuggestedAction : + mLastDisconnectSuggestedAction; + imsDisconnectRadioTech = isEmergencyCallback ? mEmergencyLastDisconnectRadioTech : + mLastDisconnectRadioTech; urisSet = mUrisSet; uris = mUris; } diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index 0e23f364134c..799ac642fca1 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -592,4 +592,17 @@ interface ISms { * @hide */ boolean resetAllCellBroadcastRanges(int subId); + + /** + * Gets the message size of a WAP from the cache. + * + * @param locationUrl the location to use as a key for looking up the size in the cache. + * The locationUrl may or may not have the transactionId appended to the url. + * + * @return long representing the message size + * @throws java.util.NoSuchElementException if the WAP push doesn't exist in the cache + * + * @hide + */ + long getWapMessageSize(String locationUrl); } diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java index 686455688203..70bc4dda2ca7 100644 --- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java +++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java @@ -227,4 +227,10 @@ public class ISmsImplBase extends ISms.Stub { public boolean resetAllCellBroadcastRanges(int subId) { throw new UnsupportedOperationException(); } + + @Override + public long getWapMessageSize(String locationUrl) { + throw new UnsupportedOperationException(); + } + } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 37752d0cecd8..84777c9441a1 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1901,6 +1901,16 @@ interface ITelephony { void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c); /** + * Adds an IMS emergency registration status callback for the subscription id specified. + */ + void registerImsEmergencyRegistrationCallback(int subId, IImsRegistrationCallback c); + /** + * Removes an existing IMS emergency registration status callback for the subscription + * id specified. + */ + void unregisterImsEmergencyRegistrationCallback(int subId, IImsRegistrationCallback c); + + /** * Get the IMS service registration state for the MmTelFeature associated with this sub id. */ void getImsMmTelRegistrationState(int subId, IIntegerConsumer consumer); @@ -3035,8 +3045,8 @@ interface ITelephony { * * @param handoverType The type of handover from emergency call to satellite messaging. Use one * of the following values to enable the override: - * 0 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS - * 1 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 + * 1 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS + * 2 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 * To disable the override, use -1 for handoverType. * @param delaySeconds The event EVENT_DISPLAY_EMERGENCY_MESSAGE will be sent to Dialer * delaySeconds after the emergency call starts. |