diff options
337 files changed, 5771 insertions, 2327 deletions
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl index 0c920f1359f3..60c2eeddac63 100644 --- a/core/java/android/app/IUidObserver.aidl +++ b/core/java/android/app/IUidObserver.aidl @@ -58,8 +58,9 @@ oneway interface IUidObserver { * Report a proc oom adj change associated with a uid. * * @param uid The uid for which the state change is being reported. + * @param adj The minimum OOM adj among all processes with this uid. */ - void onUidProcAdjChanged(int uid); + void onUidProcAdjChanged(int uid, int adj); // =============== End of transactions used on native side as well ============================ diff --git a/core/java/android/app/UidObserver.java b/core/java/android/app/UidObserver.java index 9e928073ac5c..519662448e91 100644 --- a/core/java/android/app/UidObserver.java +++ b/core/java/android/app/UidObserver.java @@ -41,7 +41,7 @@ public class UidObserver extends IUidObserver.Stub { } @Override - public void onUidProcAdjChanged(int uid) { + public void onUidProcAdjChanged(int uid, int adj) { } @Override diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 6592019dc0d9..f6733047d965 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -822,6 +822,10 @@ public class WallpaperManager { */ @TestApi public boolean isLockscreenLiveWallpaperEnabled() { + return isLockscreenLiveWallpaperEnabledHelper(); + } + + private static boolean isLockscreenLiveWallpaperEnabledHelper() { if (sGlobals == null) { sIsLockscreenLiveWallpaperEnabled = SystemProperties.getBoolean( "persist.wm.debug.lockscreen_live_wallpaper", false); @@ -2757,7 +2761,7 @@ public class WallpaperManager { public static InputStream openDefaultWallpaper(Context context, @SetWallpaperFlags int which) { final String whichProp; final int defaultResId; - if (which == FLAG_LOCK && !sIsLockscreenLiveWallpaperEnabled) { + if (which == FLAG_LOCK && !isLockscreenLiveWallpaperEnabledHelper()) { /* Factory-default lock wallpapers are not yet supported whichProp = PROP_LOCK_WALLPAPER; defaultResId = com.android.internal.R.drawable.default_lock_wallpaper; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 1ba84c5aae19..d802b46f1fde 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -502,6 +502,8 @@ interface IPackageManager { boolean hasSystemFeature(String name, int version); + List<String> getInitialNonStoppedSystemPackages(); + void enterSafeMode(); @UnsupportedAppUsage boolean isSafeMode(); diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 295df5cc42d0..be40143f5bd3 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -50,7 +50,6 @@ import android.view.contentcapture.ContentCaptureContext; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; -import java.lang.IllegalArgumentException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -285,6 +284,12 @@ public final class ShortcutInfo implements Parcelable { */ public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103; + /** + * The maximum length of Shortcut ID. IDs will be truncated at this limit. + * @hide + */ + public static final int MAX_ID_LENGTH = 1000; + /** @hide */ @IntDef(prefix = { "DISABLED_REASON_" }, value = { DISABLED_REASON_NOT_DISABLED, @@ -477,8 +482,7 @@ public final class ShortcutInfo implements Parcelable { private ShortcutInfo(Builder b) { mUserId = b.mContext.getUserId(); - - mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided"); + mId = getSafeId(Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided")); // Note we can't do other null checks here because SM.updateShortcuts() takes partial // information. @@ -584,6 +588,14 @@ public final class ShortcutInfo implements Parcelable { return ret; } + @NonNull + private static String getSafeId(@NonNull String id) { + if (id.length() > MAX_ID_LENGTH) { + return id.substring(0, MAX_ID_LENGTH); + } + return id; + } + /** * Throws if any of the mandatory fields is not set. * @@ -2342,7 +2354,8 @@ public final class ShortcutInfo implements Parcelable { final ClassLoader cl = getClass().getClassLoader(); mUserId = source.readInt(); - mId = source.readString8(); + mId = getSafeId(Preconditions.checkStringNotEmpty(source.readString8(), + "Shortcut ID must be provided")); mPackageName = source.readString8(); mActivity = source.readParcelable(cl, android.content.ComponentName.class); mFlags = source.readInt(); diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java index 888047d95f80..21fe686fa2e3 100644 --- a/core/java/android/hardware/soundtrigger/ConversionUtil.java +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -32,10 +32,12 @@ import android.media.soundtrigger.RecognitionConfig; import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionMode; import android.media.soundtrigger.SoundModel; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.os.ParcelFileDescriptor; -import android.system.ErrnoException; import android.os.SharedMemory; +import android.system.ErrnoException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -219,36 +221,40 @@ public class ConversionUtil { return new SoundTrigger.ConfidenceLevel(apiLevel.userId, apiLevel.levelPercent); } - public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent( - int modelHandle, int captureSession, RecognitionEvent aidlEvent) { + public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(int modelHandle, + int captureSession, RecognitionEventSys aidlEvent) { + RecognitionEvent recognitionEvent = aidlEvent.recognitionEvent; // The API recognition event doesn't allow for a null audio format, even though it doesn't // always make sense. We thus replace it with a default. - AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig, + AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(recognitionEvent.audioConfig, true /*isInput*/); - // TODO(b/265852186) propagate a timestamp from aidl interfaces - return new SoundTrigger.GenericRecognitionEvent(aidlEvent.status, modelHandle, - aidlEvent.captureAvailable, captureSession, aidlEvent.captureDelayMs, - aidlEvent.capturePreambleMs, aidlEvent.triggerInData, audioFormat, aidlEvent.data, - aidlEvent.recognitionStillActive, -1 /* halEventReceivedMillis */); + return new SoundTrigger.GenericRecognitionEvent(recognitionEvent.status, modelHandle, + recognitionEvent.captureAvailable, captureSession, recognitionEvent.captureDelayMs, + recognitionEvent.capturePreambleMs, recognitionEvent.triggerInData, audioFormat, + recognitionEvent.data, + recognitionEvent.recognitionStillActive, aidlEvent.halEventReceivedMillis); } public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent( - int modelHandle, int captureSession, - PhraseRecognitionEvent aidlEvent) { + int modelHandle, int captureSession, PhraseRecognitionEventSys aidlEvent) { + PhraseRecognitionEvent recognitionEvent = aidlEvent.phraseRecognitionEvent; SoundTrigger.KeyphraseRecognitionExtra[] apiExtras = - new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length]; - for (int i = 0; i < aidlEvent.phraseExtras.length; ++i) { - apiExtras[i] = aidl2apiPhraseRecognitionExtra(aidlEvent.phraseExtras[i]); + new SoundTrigger.KeyphraseRecognitionExtra[recognitionEvent.phraseExtras.length]; + for (int i = 0; i < recognitionEvent.phraseExtras.length; ++i) { + apiExtras[i] = aidl2apiPhraseRecognitionExtra(recognitionEvent.phraseExtras[i]); } // The API recognition event doesn't allow for a null audio format, even though it doesn't // always make sense. We thus replace it with a default. - AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig, + AudioFormat audioFormat = aidl2apiAudioFormatWithDefault( + recognitionEvent.common.audioConfig, true /*isInput*/); - // TODO(b/265852186) propagate a timestamp from aidl interfaces - return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle, - aidlEvent.common.captureAvailable, captureSession, aidlEvent.common.captureDelayMs, - aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData, audioFormat, - aidlEvent.common.data, apiExtras, -1 /* halEventReceivedMillis */); + return new SoundTrigger.KeyphraseRecognitionEvent(recognitionEvent.common.status, + modelHandle, + recognitionEvent.common.captureAvailable, captureSession, + recognitionEvent.common.captureDelayMs, + recognitionEvent.common.capturePreambleMs, recognitionEvent.common.triggerInData, + audioFormat, + recognitionEvent.common.data, apiExtras, aidlEvent.halEventReceivedMillis); } // In case of a null input returns a non-null valid output. diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java index 37c5213e0359..5cdbe233aa3b 100644 --- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java +++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java @@ -22,13 +22,13 @@ import android.compat.annotation.UnsupportedAppUsage; import android.media.permission.ClearCallingIdentityContext; import android.media.permission.Identity; import android.media.permission.SafeCloseable; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -398,7 +398,7 @@ public class SoundTriggerModule { } @Override - public synchronized void onRecognition(int handle, RecognitionEvent event, + public synchronized void onRecognition(int handle, RecognitionEventSys event, int captureSession) throws RemoteException { Message m = mHandler.obtainMessage(EVENT_RECOGNITION, @@ -407,7 +407,7 @@ public class SoundTriggerModule { } @Override - public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event, + public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEventSys event, int captureSession) throws RemoteException { Message m = mHandler.obtainMessage(EVENT_RECOGNITION, diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 24e28e95cd98..5bcbaa10e95b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -65,7 +65,6 @@ import android.util.Log; import android.view.WindowManager.LayoutParams; import com.android.internal.R; -import com.android.internal.util.FrameworkStatsLog; import java.io.IOException; import java.lang.annotation.Retention; @@ -2533,38 +2532,6 @@ public class UserManager { } /** - * Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to the - * user type. - * @hide - */ - public static int getUserTypeForStatsd(@NonNull String userType) { - switch (userType) { - case USER_TYPE_FULL_SYSTEM: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SYSTEM; - case USER_TYPE_FULL_SECONDARY: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY; - case USER_TYPE_FULL_GUEST: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_GUEST; - case USER_TYPE_FULL_DEMO: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_DEMO; - case USER_TYPE_FULL_RESTRICTED: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_RESTRICTED; - case USER_TYPE_PROFILE_MANAGED: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_MANAGED; - case USER_TYPE_SYSTEM_HEADLESS: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS; - case USER_TYPE_PROFILE_CLONE: - return FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE; - default: - return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN; - } - } - - /** * @hide * @deprecated Use {@link #isRestrictedProfile()} */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4a46beb670de..329a2fa57c08 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7175,6 +7175,13 @@ public final class Settings { public static final String CREDENTIAL_SERVICE = "credential_service"; /** + * The currently selected primary credential service flattened ComponentName. + * + * @hide + */ + public static final String CREDENTIAL_SERVICE_PRIMARY = "credential_service_primary"; + + /** * The currently selected autofill service flattened ComponentName. * @hide */ diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index e39b3a182b28..6ff4b74c68a6 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1477,14 +1477,22 @@ public final class AutofillManager { // to PCC classification service. if (AutofillFeatureFlags.isAutofillPccClassificationEnabled()) { synchronized (mLock) { - final boolean clientAdded = tryAddServiceClientIfNeededLocked(); - if (clientAdded){ - startSessionLocked(/* id= */ AutofillId.NO_AUTOFILL_ID, - /* bounds= */ null, /* value= */ null, /* flags= */ FLAG_PCC_DETECTION); - } else { - if (sVerbose) { - Log.v(TAG, "not starting session: no service client"); + // If session has already been created, that'd mean we already have issued the + // detection request previously. It is possible in cases like autofocus that this + // method isn't invoked, so the server should still handle such cases where fill + // request comes in but PCC Detection hasn't been triggered. There is no benefit to + // trigger PCC Detection separately in those cases. + if (!isActiveLocked()) { + final boolean clientAdded = tryAddServiceClientIfNeededLocked(); + if (clientAdded) { + startSessionLocked(/* id= */ AutofillId.NO_AUTOFILL_ID, /* bounds= */ null, + /* value= */ null, /* flags= */ FLAG_PCC_DETECTION); + } else { + if (sVerbose) { + Log.v(TAG, "not starting session: no service client"); + } } + } } } diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto index 3a9e5eeb4877..02ba7c5e39ff 100644 --- a/core/proto/android/companion/telecom.proto +++ b/core/proto/android/companion/telecom.proto @@ -25,7 +25,7 @@ message Telecom { // Next index: 6 message Call { // UUID representing this call - int64 id = 1; + string id = 1; message Origin { // Caller's name and/or phone number; what a user would see displayed when receiving an @@ -48,22 +48,23 @@ message Telecom { } Status status = 3; - enum Control { - UNKNOWN_CONTROL = 0; - ACCEPT = 1; - REJECT = 2; - SILENCE = 3; - MUTE = 4; - UNMUTE = 5; - END = 6; - PUT_ON_HOLD = 7; - TAKE_OFF_HOLD = 8; - REJECT_AND_BLOCK = 9; - IGNORE = 10; - } repeated Control controls = 4; } + enum Control { + UNKNOWN_CONTROL = 0; + ACCEPT = 1; + REJECT = 2; + SILENCE = 3; + MUTE = 4; + UNMUTE = 5; + END = 6; + PUT_ON_HOLD = 7; + TAKE_OFF_HOLD = 8; + REJECT_AND_BLOCK = 9; + IGNORE = 10; + } + // The list of active calls. repeated Call calls = 1; // The list of requested calls or call changes. diff --git a/core/res/res/drawable-hdpi/pointer_context_menu.png b/core/res/res/drawable-hdpi/pointer_context_menu.png Binary files differindex c45d29baed21..60d37c4ad767 100644 --- a/core/res/res/drawable-hdpi/pointer_context_menu.png +++ b/core/res/res/drawable-hdpi/pointer_context_menu.png diff --git a/core/res/res/drawable-hdpi/pointer_help.png b/core/res/res/drawable-hdpi/pointer_help.png Binary files differindex a3afdb695897..2d9d20c7f3e2 100644 --- a/core/res/res/drawable-hdpi/pointer_help.png +++ b/core/res/res/drawable-hdpi/pointer_help.png diff --git a/core/res/res/drawable-mdpi/pointer_context_menu.png b/core/res/res/drawable-mdpi/pointer_context_menu.png Binary files differindex e0e849d0bff7..d87d040004e2 100644 --- a/core/res/res/drawable-mdpi/pointer_context_menu.png +++ b/core/res/res/drawable-mdpi/pointer_context_menu.png diff --git a/core/res/res/drawable-mdpi/pointer_context_menu_large.png b/core/res/res/drawable-mdpi/pointer_context_menu_large.png Binary files differindex e8c9be4bfa57..15266a625b4c 100644 --- a/core/res/res/drawable-mdpi/pointer_context_menu_large.png +++ b/core/res/res/drawable-mdpi/pointer_context_menu_large.png diff --git a/core/res/res/drawable-mdpi/pointer_help.png b/core/res/res/drawable-mdpi/pointer_help.png Binary files differindex 286242cfc514..bd04bbd351b7 100644 --- a/core/res/res/drawable-mdpi/pointer_help.png +++ b/core/res/res/drawable-mdpi/pointer_help.png diff --git a/core/res/res/drawable-mdpi/pointer_help_large.png b/core/res/res/drawable-mdpi/pointer_help_large.png Binary files differindex 27f4a845e06c..f9bd2b78ac5a 100644 --- a/core/res/res/drawable-mdpi/pointer_help_large.png +++ b/core/res/res/drawable-mdpi/pointer_help_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_context_menu.png b/core/res/res/drawable-xhdpi/pointer_context_menu.png Binary files differindex d4b2bdeb2cff..15d1e33ac421 100644 --- a/core/res/res/drawable-xhdpi/pointer_context_menu.png +++ b/core/res/res/drawable-xhdpi/pointer_context_menu.png diff --git a/core/res/res/drawable-xhdpi/pointer_context_menu_large.png b/core/res/res/drawable-xhdpi/pointer_context_menu_large.png Binary files differindex 977df10c20d5..e4ff7a625eac 100644 --- a/core/res/res/drawable-xhdpi/pointer_context_menu_large.png +++ b/core/res/res/drawable-xhdpi/pointer_context_menu_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_help.png b/core/res/res/drawable-xhdpi/pointer_help.png Binary files differindex 5a6805c47772..952a4ee82e12 100644 --- a/core/res/res/drawable-xhdpi/pointer_help.png +++ b/core/res/res/drawable-xhdpi/pointer_help.png diff --git a/core/res/res/drawable-xhdpi/pointer_help_large.png b/core/res/res/drawable-xhdpi/pointer_help_large.png Binary files differindex 4bdc3d14d5a3..1d68437742d2 100644 --- a/core/res/res/drawable-xhdpi/pointer_help_large.png +++ b/core/res/res/drawable-xhdpi/pointer_help_large.png diff --git a/core/res/res/drawable-xxhdpi/pointer_context_menu.png b/core/res/res/drawable-xxhdpi/pointer_context_menu.png Binary files differindex 6ebfaab9abed..4cd20f5a0d08 100644 --- a/core/res/res/drawable-xxhdpi/pointer_context_menu.png +++ b/core/res/res/drawable-xxhdpi/pointer_context_menu.png diff --git a/core/res/res/drawable-xxhdpi/pointer_help.png b/core/res/res/drawable-xxhdpi/pointer_help.png Binary files differindex 96b2a711d9e7..0c7a264a9d2e 100644 --- a/core/res/res/drawable-xxhdpi/pointer_help.png +++ b/core/res/res/drawable-xxhdpi/pointer_help.png diff --git a/core/res/res/drawable/pointer_context_menu_large_icon.xml b/core/res/res/drawable/pointer_context_menu_large_icon.xml index e07e5b607d65..325ea6696ec7 100644 --- a/core/res/res/drawable/pointer_context_menu_large_icon.xml +++ b/core/res/res/drawable/pointer_context_menu_large_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_context_menu_large" - android:hotSpotX="13.5dp" - android:hotSpotY="10.5dp" /> + android:hotSpotX="10.5dp" + android:hotSpotY="8dp" /> diff --git a/core/res/res/drawable/pointer_help_large_icon.xml b/core/res/res/drawable/pointer_help_large_icon.xml index 43a12616a540..20f0c53362a1 100644 --- a/core/res/res/drawable/pointer_help_large_icon.xml +++ b/core/res/res/drawable/pointer_help_large_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_help_large" - android:hotSpotX="13.5dp" - android:hotSpotY="10.5dp" /> + android:hotSpotX="10.5dp" + android:hotSpotY="8dp" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7bc3ab80da99..50aec835cc31 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -991,6 +991,25 @@ <!-- Nominal White Z --> <item>1.089058</item> </string-array> + <!-- The CCT closest to the white coordinates (primary) above and in SurfaceControl. --> + <integer name="config_displayWhiteBalanceDisplayNominalWhiteCct">6500</integer> + + <!-- Range minimums corresponding to config_displayWhiteBalanceDisplaySteps. For example, if the + range minimums are [0, 3000] and the steps are [10, 20] then between 0 and 3000, exclusive, + the step between them will be 10 (i.e. 0, 10, 20, etc.) and the step between 3000 and the + maximum value is 20 (i.e. 3000, 3020, 3040, etc.). --> + <integer-array name="config_displayWhiteBalanceDisplayRangeMinimums"> + <item>0</item> + </integer-array> + + <!-- Steps corresponding to config_displayWhiteBalanceDisplayRangeMinimums. For example, if the + range minimums are [0, 3000] and the steps are [10, 20] then between 0 and 3000, exclusive, + the step between them will be 10 (i.e. 0, 10, 20, etc.) and the step between 3000 and the + maximum value is 20 (i.e. 3000, 3020, 3040, etc.). --> + <integer-array name="config_displayWhiteBalanceDisplaySteps"> + <item>1</item> + </integer-array> + <!-- Boolean indicating whether light mode is allowed when DWB is turned on. --> <bool name="config_displayWhiteBalanceLightModeAllowed">true</bool> @@ -6396,7 +6415,7 @@ Packages can be added by OEMs in an allowlist, to prevent them from being scanned as "stopped" during initial boot of a device, or after an OTA update. Stopped state of an app is not changed during subsequent reboots. --> - <bool name="config_stopSystemPackagesByDefault">false</bool> + <bool name="config_stopSystemPackagesByDefault">true</bool> <!-- Whether to show weather on the lock screen by default. --> <bool name="config_lockscreenWeatherEnabledByDefault">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e25425d6cb18..218bbc24bbe6 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3423,6 +3423,9 @@ <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureDefault" /> <java-symbol type="array" name="config_displayWhiteBalanceDisplayPrimaries" /> <java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" /> + <java-symbol type="integer" name="config_displayWhiteBalanceDisplayNominalWhiteCct" /> + <java-symbol type="array" name="config_displayWhiteBalanceDisplayRangeMinimums" /> + <java-symbol type="array" name="config_displayWhiteBalanceDisplaySteps" /> <java-symbol type="bool" name="config_displayWhiteBalanceLightModeAllowed" /> <java-symbol type="integer" name="config_displayWhiteBalanceTransitionTime" /> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 40cb7f2194e9..1f1239e3c310 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -520,6 +520,10 @@ applications that come with the platform <permission name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"/> <!-- Permission required for CTS test - SatelliteManagerTest --> <permission name="android.permission.SATELLITE_COMMUNICATION"/> + <!-- Permission required for GTS test - GtsAttestationVerificationDeviceSideTestCases --> + <permission name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" /> + <!-- Permission required for GTS test - GtsCredentialsTestCases --> + <permission name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 6a79bc1f46c2..54978bd4496d 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -125,34 +125,6 @@ prebuilt_etc { // End ProtoLog -gensrcs { - name: "wm-shell-protos", - - tools: [ - "aprotoc", - "protoc-gen-javastream", - "soong_zip", - ], - - tool_files: [ - ":libprotobuf-internal-protos", - ], - - cmd: "mkdir -p $(genDir)/$(in) " + - "&& $(location aprotoc) " + - " --plugin=$(location protoc-gen-javastream) " + - " --javastream_out=$(genDir)/$(in) " + - " -Iexternal/protobuf/src " + - " -I . " + - " $(in) " + - "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)", - - srcs: [ - "proto/**/*.proto", - ], - output_extension: "srcjar", -} - java_library { name: "WindowManager-Shell-proto", @@ -170,7 +142,6 @@ android_library { // TODO(b/168581922) protologtool do not support kotlin(*.kt) ":wm_shell-sources-kt", ":wm_shell-aidls", - ":wm-shell-protos", ], resource_dirs: [ "res", diff --git a/libs/WindowManager/Shell/res/color/taskbar_background.xml b/libs/WindowManager/Shell/res/color/taskbar_background.xml deleted file mode 100644 index 876ee02a8adf..000000000000 --- a/libs/WindowManager/Shell/res/color/taskbar_background.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<!-- Should be the same as in packages/apps/Launcher3/res/color-v31/taskbar_background.xml --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@android:color/system_neutral1_500" android:lStar="98" /> -</selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color-night/taskbar_background.xml b/libs/WindowManager/Shell/res/color/taskbar_background_dark.xml index 01df006f1bd2..01df006f1bd2 100644 --- a/libs/WindowManager/Shell/res/color-night/taskbar_background.xml +++ b/libs/WindowManager/Shell/res/color/taskbar_background_dark.xml diff --git a/libs/WindowManager/Shell/res/values-night/colors.xml b/libs/WindowManager/Shell/res/values-night/colors.xml index 5c6bb57a7f1c..83c4d93982f4 100644 --- a/libs/WindowManager/Shell/res/values-night/colors.xml +++ b/libs/WindowManager/Shell/res/values-night/colors.xml @@ -15,7 +15,6 @@ --> <resources> - <color name="docked_divider_handle">#ffffff</color> <!-- Bubbles --> <color name="bubbles_icon_tint">@color/GM2_grey_200</color> <!-- Splash screen--> diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index c487e4afd678..54a8f33fd679 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -17,8 +17,8 @@ */ --> <resources> - <color name="docked_divider_handle">#000000</color> - <color name="split_divider_background">@color/taskbar_background</color> + <color name="docked_divider_handle">#ffffff</color> + <color name="split_divider_background">@color/taskbar_background_dark</color> <drawable name="forced_resizable_background">#59000000</drawable> <color name="minimize_dock_shadow_start">#60000000</color> <color name="minimize_dock_shadow_end">#00000000</color> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index f3efaade945f..e7ec7aa164e9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -3320,7 +3320,9 @@ public class BubbleStackView extends FrameLayout * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedXPosition() { - return new BigDecimal(getStackPosition().x / mPositioner.getAvailableRect().width()) + int width = mPositioner.getAvailableRect().width(); + float stackPosition = width > 0 ? getStackPosition().x / width : 0; + return new BigDecimal(stackPosition) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } @@ -3329,7 +3331,9 @@ public class BubbleStackView extends FrameLayout * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedYPosition() { - return new BigDecimal(getStackPosition().y / mPositioner.getAvailableRect().height()) + int height = mPositioner.getAvailableRect().height(); + float stackPosition = height > 0 ? getStackPosition().y / height : 0; + return new BigDecimal(stackPosition) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 80e920fefe5f..28368ef37061 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -189,12 +189,13 @@ public abstract class WMShellBaseModule { static Optional<DragAndDropController> provideDragAndDropController(Context context, ShellInit shellInit, ShellController shellController, + ShellCommandHandler shellCommandHandler, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable(DragAndDropController.create(context, shellInit, shellController, - displayController, uiEventLogger, iconProvider, mainExecutor)); + shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor)); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index fb0a91f17802..0f0d572f8eae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -162,7 +162,7 @@ public class DesktopModeVisualIndicator { /** * Release the indicator and its components when it is no longer needed. */ - public void releaseVisualIndicator() { + public void releaseVisualIndicator(SurfaceControl.Transaction t) { if (mViewHost == null) return; if (mViewHost != null) { mViewHost.release(); @@ -170,13 +170,8 @@ public class DesktopModeVisualIndicator { } if (mLeash != null) { - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.remove(mLeash); mLeash = null; - mSyncQueue.runInSync(transaction -> { - transaction.merge(t); - t.close(); - }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 73a0e362a744..ee94f30cfa69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -78,6 +78,11 @@ class DesktopTasksController( private val desktopMode: DesktopModeImpl private var visualIndicator: DesktopModeVisualIndicator? = null + private val mOnAnimationFinishedCallback = Consumer<SurfaceControl.Transaction> { + t: SurfaceControl.Transaction -> + visualIndicator?.releaseVisualIndicator(t) + visualIndicator = null + } init { desktopMode = DesktopModeImpl() @@ -154,14 +159,14 @@ class DesktopTasksController( if (Transitions.ENABLE_SHELL_TRANSITIONS) { enterDesktopTaskTransitionHandler.startTransition( - Transitions.TRANSIT_ENTER_FREEFORM, wct) + Transitions.TRANSIT_ENTER_FREEFORM, wct, mOnAnimationFinishedCallback) } else { shellTaskOrganizer.applyTransaction(wct) } } /** Brings apps to front and sets freeform task bounds */ - fun moveToDesktopWithAnimation( + private fun moveToDesktopWithAnimation( taskInfo: RunningTaskInfo, freeformBounds: Rect ) { @@ -172,9 +177,10 @@ class DesktopTasksController( if (Transitions.ENABLE_SHELL_TRANSITIONS) { enterDesktopTaskTransitionHandler.startTransition( - Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct) + Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct, mOnAnimationFinishedCallback) } else { shellTaskOrganizer.applyTransaction(wct) + releaseVisualIndicator() } } @@ -205,21 +211,24 @@ class DesktopTasksController( val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { - enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, startPosition) + enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, startPosition, + mOnAnimationFinishedCallback) } else { shellTaskOrganizer.applyTransaction(wct) + releaseVisualIndicator() } } - fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) { + private fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) { val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { exitDesktopTaskTransitionHandler.startTransition( - Transitions.TRANSIT_EXIT_DESKTOP_MODE, wct) + Transitions.TRANSIT_EXIT_DESKTOP_MODE, wct, mOnAnimationFinishedCallback) } else { shellTaskOrganizer.applyTransaction(wct) + releaseVisualIndicator() } } @@ -330,6 +339,16 @@ class DesktopTasksController( ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } } + private fun releaseVisualIndicator() { + val t = SurfaceControl.Transaction() + visualIndicator?.releaseVisualIndicator(t) + visualIndicator = null + syncQueue.runInSync { transaction -> + transaction.merge(t) + t.close() + } + } + override fun getContext(): Context { return context } @@ -471,8 +490,7 @@ class DesktopTasksController( rootTaskDisplayAreaOrganizer) visualIndicator?.createFullscreenIndicatorWithAnimatedBounds() } else if (y > statusBarHeight && visualIndicator != null) { - visualIndicator?.releaseVisualIndicator() - visualIndicator = null + releaseVisualIndicator() } } } @@ -489,8 +507,6 @@ class DesktopTasksController( ) { val statusBarHeight = getStatusBarHeight(taskInfo) if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { - visualIndicator?.releaseVisualIndicator() - visualIndicator = null moveToFullscreenWithAnimation(taskInfo) } } @@ -508,6 +524,11 @@ class DesktopTasksController( taskSurface: SurfaceControl, y: Float ) { + // If the motion event is above the status bar, return since we do not need to show the + // visual indicator at this point. + if (y < getStatusBarHeight(taskInfo)) { + return + } if (visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController, context, taskSurface, shellTaskOrganizer, @@ -535,11 +556,8 @@ class DesktopTasksController( freeformBounds: Rect ) { moveToDesktopWithAnimation(taskInfo, freeformBounds) - visualIndicator?.releaseVisualIndicator() - visualIndicator = null } - private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int { return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0 } @@ -566,7 +584,6 @@ class DesktopTasksController( desktopModeTaskRepository.removeTaskCorners(taskId) } - /** * Adds a listener to find out about changes in the visibility of freeform tasks. * diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index 94675788c514..d55fdddf4fea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -39,6 +39,7 @@ import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -58,6 +59,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); private Point mStartPosition; + private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback; public EnterDesktopTaskTransitionHandler( Transitions transitions) { @@ -75,9 +77,12 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition * Starts Transition of a given type * @param type Transition type * @param wct WindowContainerTransaction for transition + * @param onAnimationEndCallback to be called after animation */ public void startTransition(@WindowManager.TransitionType int type, - @NonNull WindowContainerTransaction wct) { + @NonNull WindowContainerTransaction wct, + Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { + mOnAnimationFinishedCallback = onAnimationEndCallback; final IBinder token = mTransitions.startTransition(type, wct, this); mPendingTransitionTokens.add(token); } @@ -86,11 +91,14 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE * @param wct WindowContainerTransaction for transition * @param startPosition Position of task when transition is triggered + * @param onAnimationEndCallback to be called after animation */ public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct, - Point startPosition) { + Point startPosition, + Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { mStartPosition = startPosition; - startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct); + startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct, + mOnAnimationFinishedCallback); } @Override @@ -111,7 +119,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition if (change.getMode() == WindowManager.TRANSIT_CHANGE) { transitionHandled |= startChangeTransition( - transition, info.getType(), change, startT, finishCallback); + transition, info.getType(), change, startT, finishT, finishCallback); } } @@ -125,6 +133,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change, @NonNull SurfaceControl.Transaction startT, + @NonNull SurfaceControl.Transaction finishT, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (!mPendingTransitionTokens.contains(transition)) { return false; @@ -178,6 +187,9 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + if (mOnAnimationFinishedCallback != null) { + mOnAnimationFinishedCallback.accept(finishT); + } mTransitions.getMainExecutor().execute( () -> finishCallback.onTransitionFinished(null, null)); } @@ -204,7 +216,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition animator.setDuration(FREEFORM_ANIMATION_DURATION); final SurfaceControl.Transaction t = mTransactionSupplier.get(); animator.addUpdateListener(animation -> { - final float scale = animation.getAnimatedFraction(); + final float scale = (float) animation.getAnimatedValue(); t.setPosition(sc, mStartPosition.x * (1 - scale), mStartPosition.y * (1 - scale)) .setScale(sc, scale, scale) .show(sc) @@ -213,6 +225,9 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + if (mOnAnimationFinishedCallback != null) { + mOnAnimationFinishedCallback.accept(finishT); + } mTransitions.getMainExecutor().execute( () -> finishCallback.onTransitionFinished(null, null)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java index fa3eee257a52..160a83d7ed36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java @@ -42,6 +42,7 @@ import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; @@ -54,7 +55,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH private final Context mContext; private final Transitions mTransitions; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); - + private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback; private Supplier<SurfaceControl.Transaction> mTransactionSupplier; public ExitDesktopTaskTransitionHandler( @@ -76,9 +77,12 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH * Starts Transition of a given type * @param type Transition type * @param wct WindowContainerTransaction for transition + * @param onAnimationEndCallback to be called after animation */ public void startTransition(@WindowManager.TransitionType int type, - @NonNull WindowContainerTransaction wct) { + @NonNull WindowContainerTransaction wct, + Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { + mOnAnimationFinishedCallback = onAnimationEndCallback; final IBinder token = mTransitions.startTransition(type, wct, this); mPendingTransitionTokens.add(token); } @@ -101,7 +105,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH if (change.getMode() == WindowManager.TRANSIT_CHANGE) { transitionHandled |= startChangeTransition( - transition, info.getType(), change, startT, finishCallback); + transition, info.getType(), change, startT, finishT, finishCallback); } } @@ -116,6 +120,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change, @NonNull SurfaceControl.Transaction startT, + @NonNull SurfaceControl.Transaction finishT, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (!mPendingTransitionTokens.contains(transition)) { return false; @@ -156,6 +161,9 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + if (mOnAnimationFinishedCallback != null) { + mOnAnimationFinishedCallback.accept(finishT); + } mTransitions.getMainExecutor().execute( () -> finishCallback.onTransitionFinished(null, null)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 091de3a86461..be2489da3628 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -35,10 +35,14 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP; + import android.content.ClipDescription; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; +import android.graphics.HardwareRenderer; import android.graphics.PixelFormat; import android.util.Slog; import android.util.SparseArray; @@ -50,6 +54,8 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; +import androidx.annotation.BinderThread; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.InstanceId; @@ -58,25 +64,31 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ExternalInterfaceBinder; +import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalMainThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import java.io.PrintWriter; import java.util.ArrayList; /** * Handles the global drag and drop handling for the Shell. */ -public class DragAndDropController implements DisplayController.OnDisplaysChangedListener, +public class DragAndDropController implements RemoteCallable<DragAndDropController>, + DisplayController.OnDisplaysChangedListener, View.OnDragListener, ComponentCallbacks2 { private static final String TAG = DragAndDropController.class.getSimpleName(); private final Context mContext; private final ShellController mShellController; + private final ShellCommandHandler mShellCommandHandler; private final DisplayController mDisplayController; private final DragAndDropEventLogger mLogger; private final IconProvider mIconProvider; @@ -100,6 +112,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange public static DragAndDropController create(Context context, ShellInit shellInit, ShellController shellController, + ShellCommandHandler shellCommandHandler, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, @@ -107,19 +120,21 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange if (!context.getResources().getBoolean(R.bool.config_enableShellDragDrop)) { return null; } - return new DragAndDropController(context, shellInit, shellController, displayController, - uiEventLogger, iconProvider, mainExecutor); + return new DragAndDropController(context, shellInit, shellController, shellCommandHandler, + displayController, uiEventLogger, iconProvider, mainExecutor); } DragAndDropController(Context context, ShellInit shellInit, ShellController shellController, + ShellCommandHandler shellCommandHandler, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, ShellExecutor mainExecutor) { mContext = context; mShellController = shellController; + mShellCommandHandler = shellCommandHandler; mDisplayController = displayController; mLogger = new DragAndDropEventLogger(uiEventLogger); mIconProvider = iconProvider; @@ -137,6 +152,23 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mMainExecutor.executeDelayed(() -> { mDisplayController.addDisplayWindowListener(this); }, 0); + mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP, + this::createExternalInterface, this); + mShellCommandHandler.addDumpCallback(this::dump, this); + } + + private ExternalInterfaceBinder createExternalInterface() { + return new IDragAndDropImpl(this); + } + + @Override + public Context getContext() { + return mContext; + } + + @Override + public ShellExecutor getRemoteCallExecutor() { + return mMainExecutor; } /** @@ -156,7 +188,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mListeners.remove(listener); } - private void notifyListeners() { + private void notifyDragStarted() { for (int i = 0; i < mListeners.size(); i++) { mListeners.get(i).onDragStarted(); } @@ -273,7 +305,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange pd.dragLayout.prepare(mDisplayController.getDisplayLayout(displayId), event.getClipData(), loggerSessionId); setDropTargetWindowVisibility(pd, View.VISIBLE); - notifyListeners(); + notifyDragStarted(); break; case ACTION_DRAG_ENTERED: pd.dragLayout.show(); @@ -327,13 +359,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange } private void setDropTargetWindowVisibility(PerDisplay pd, int visibility) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, - "Set drop target window visibility: displayId=%d visibility=%d", - pd.displayId, visibility); - pd.rootView.setVisibility(visibility); - if (visibility == View.VISIBLE) { - pd.rootView.requestApplyInsets(); - } + pd.setWindowVisibility(visibility); } private String getMimeTypes(ClipDescription description) { @@ -347,6 +373,18 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange return mimeTypes; } + /** + * Returns if any displays are currently ready to handle a drag/drop. + */ + private boolean isReadyToHandleDrag() { + for (int i = 0; i < mDisplayDropTargets.size(); i++) { + if (mDisplayDropTargets.valueAt(i).mHasDrawn) { + return true; + } + } + return false; + } + // Note: Component callbacks are always called on the main thread of the process @ExternalMainThread @Override @@ -372,12 +410,53 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange // Do nothing } - private static class PerDisplay { + /** + * Dumps information about this controller. + */ + public void dump(@NonNull PrintWriter pw, String prefix) { + pw.println(prefix + TAG); + pw.println(prefix + " listeners=" + mListeners.size()); + } + + /** + * The interface for calls from outside the host process. + */ + @BinderThread + private static class IDragAndDropImpl extends IDragAndDrop.Stub + implements ExternalInterfaceBinder { + private DragAndDropController mController; + + public IDragAndDropImpl(DragAndDropController controller) { + mController = controller; + } + + /** + * Invalidates this instance, preventing future calls from updating the controller. + */ + @Override + public void invalidate() { + mController = null; + } + + @Override + public boolean isReadyToHandleDrag() { + boolean[] result = new boolean[1]; + executeRemoteCallWithTaskPermission(mController, "isReadyToHandleDrag", + controller -> result[0] = controller.isReadyToHandleDrag(), + true /* blocking */ + ); + return result[0]; + } + } + + private static class PerDisplay implements HardwareRenderer.FrameDrawingCallback { final int displayId; final Context context; final WindowManager wm; final FrameLayout rootView; final DragLayout dragLayout; + // Tracks whether the window has fully drawn since it was last made visible + boolean mHasDrawn; boolean isHandlingDrag; // A count of the number of active drags in progress to ensure that we only hide the window @@ -391,5 +470,25 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange rootView = rv; dragLayout = dl; } + + private void setWindowVisibility(int visibility) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Set drop target window visibility: displayId=%d visibility=%d", + displayId, visibility); + rootView.setVisibility(visibility); + if (visibility == View.VISIBLE) { + rootView.requestApplyInsets(); + if (!mHasDrawn && rootView.getViewRootImpl() != null) { + rootView.getViewRootImpl().registerRtFrameCallback(this); + } + } else { + mHasDrawn = false; + } + } + + @Override + public void onFrameDraw(long frame) { + mHasDrawn = true; + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java index 28f59b53b5b6..724a130ef52d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java @@ -104,7 +104,7 @@ public class DropZoneView extends FrameLayout { setContainerMargin(0, 0, 0, 0); // make sure it's populated mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); - mMarginColor = getResources().getColor(R.color.taskbar_background); + mMarginColor = getResources().getColor(R.color.taskbar_background_dark); int c = getResources().getColor(android.R.color.system_accent1_500); mHighlightColor = Color.argb(HIGHLIGHT_ALPHA, Color.red(c), Color.green(c), Color.blue(c)); mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, 0, 0, 0); @@ -125,7 +125,7 @@ public class DropZoneView extends FrameLayout { public void onThemeChange() { mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(getContext()); - mMarginColor = getResources().getColor(R.color.taskbar_background); + mMarginColor = getResources().getColor(R.color.taskbar_background_dark); mHighlightColor = getResources().getColor(android.R.color.system_accent1_500); if (mMarginPercent > 0) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/IDragAndDrop.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/IDragAndDrop.aidl new file mode 100644 index 000000000000..aeb0c63fa4c5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/IDragAndDrop.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.draganddrop; + +/** + * Interface that is exposed to remote callers to manipulate drag and drop. + */ +interface IDragAndDrop { + /** + * Returns whether the shell drop target is showing and will handle a drag/drop. + */ + boolean isReadyToHandleDrag() = 1; +} +// Last id = 1
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index d3e7f9ca4227..6cedcf534f3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -27,6 +27,7 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START; @@ -524,12 +525,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } - final Rect destinationBounds = getExitDestinationBounds(); + final Rect displayBounds = mPipBoundsState.getDisplayBounds(); + final Rect destinationBounds = new Rect(displayBounds); final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit) ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_LEAVE_PIP; + // For exiting to fullscreen, the windowing mode of task will be changed to fullscreen + // until the animation is finished. Otherwise if the activity is resumed and focused at the + // begin of aniamtion, the app may do something too early to distub the animation. + final boolean toFullscreen = destinationBounds.equals(displayBounds); - if (Transitions.ENABLE_SHELL_TRANSITIONS && direction == TRANSITION_DIRECTION_LEAVE_PIP) { + if (Transitions.SHELL_TRANSITIONS_ROTATION || (Transitions.ENABLE_SHELL_TRANSITIONS + && !toFullscreen)) { // When exit to fullscreen with Shell transition enabled, we update the Task windowing // mode directly so that it can also trigger display rotation and visibility update in // the same transition if there will be any. @@ -605,7 +612,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, removePip(); } - private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) { + void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) { // Reset the final windowing mode. wct.setWindowingMode(mToken, getOutPipWindowingMode()); // Simply reset the activity mode set prior to the animation running. @@ -1771,14 +1778,26 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * @return {@code true} if destinationBounds is altered for split screen */ private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) { - if (!enterSplit || !mSplitScreenOptional.isPresent()) { + if (mSplitScreenOptional.isEmpty()) { + return false; + } + final SplitScreenController split = mSplitScreenOptional.get(); + final int position = mTaskInfo.lastParentTaskIdBeforePip > 0 + ? split.getSplitPosition(mTaskInfo.lastParentTaskIdBeforePip) + : SPLIT_POSITION_UNDEFINED; + if (position == SPLIT_POSITION_UNDEFINED && !enterSplit) { return false; } final Rect topLeft = new Rect(); final Rect bottomRight = new Rect(); - mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight); - destinationBoundsOut.set(isPipToTopLeft() ? topLeft : bottomRight); - return true; + split.getStageBounds(topLeft, bottomRight); + if (enterSplit) { + destinationBoundsOut.set(isPipToTopLeft() ? topLeft : bottomRight); + return true; + } + // Moving to an existing split task. + destinationBoundsOut.set(position == SPLIT_POSITION_TOP_OR_LEFT ? topLeft : bottomRight); + return false; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 99cb6f782d01..98db707d1105 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -508,8 +508,16 @@ public class PipTransition extends PipTransitionController { currentBounds.offset(-offset.x, -offset.y); startTransaction.setPosition(pipLeash, currentBounds.left, currentBounds.top); + final WindowContainerToken pipTaskToken = pipChange.getContainer(); + final boolean toFullscreen = pipChange.getEndAbsBounds().equals( + mPipBoundsState.getDisplayBounds()); mFinishCallback = (wct, wctCB) -> { mPipOrganizer.onExitPipFinished(taskInfo); + if (!Transitions.SHELL_TRANSITIONS_ROTATION && toFullscreen) { + wct = wct != null ? wct : new WindowContainerTransaction(); + wct.setBounds(pipTaskToken, null); + mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP); + } finishCallback.onTransitionFinished(wct, wctCB); }; mFinishTransaction = finishTransaction; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java index bfa63909cd47..5f54f58557d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java @@ -42,4 +42,6 @@ public class ShellSharedConstants { public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; // See IDesktopMode.aidl public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; + // See IDragAndDrop.aidl + public static final String KEY_EXTRA_SHELL_DRAG_AND_DROP = "extra_shell_drag_and_drop"; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index c7e534a2bcf5..2faed3a4b93d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -202,6 +202,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { } void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) { + if (mTaskViews.get(taskView) == null) return; if (mTaskViews.get(taskView).mVisible == visible) return; if (taskView.getTaskInfo() == null) { // Nothing to update, task is not yet available @@ -220,17 +221,19 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { void updateBoundsState(TaskViewTaskController taskView, Rect boundsOnScreen) { TaskViewRequestedState state = mTaskViews.get(taskView); + if (state == null) return; state.mBounds.set(boundsOnScreen); } void updateVisibilityState(TaskViewTaskController taskView, boolean visible) { TaskViewRequestedState state = mTaskViews.get(taskView); + if (state == null) return; state.mVisible = visible; } void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) { TaskViewRequestedState state = mTaskViews.get(taskView); - if (Objects.equals(boundsOnScreen, state.mBounds)) { + if (state == null || Objects.equals(boundsOnScreen, state.mBounds)) { return; } state.mBounds.set(boundsOnScreen); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java index ba364f8a6e59..0cede902f034 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java @@ -18,25 +18,29 @@ package com.android.wm.shell.transition; import static android.os.Build.IS_USER; -import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER; -import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER_H; -import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER_L; +import static com.android.wm.shell.nano.WmShellTransitionTraceProto.MAGIC_NUMBER_H; +import static com.android.wm.shell.nano.WmShellTransitionTraceProto.MAGIC_NUMBER_L; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.SystemClock; import android.os.Trace; import android.util.Log; -import android.util.proto.ProtoOutputStream; import com.android.internal.util.TraceBuffer; +import com.android.wm.shell.nano.HandlerMapping; import com.android.wm.shell.sysui.ShellCommandHandler; +import com.google.protobuf.nano.MessageNano; + import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Queue; /** * Helper class to collect and dump transition traces. @@ -54,8 +58,35 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { private final Object mEnabledLock = new Object(); private boolean mActiveTracingEnabled = false; - private final TraceBuffer mTraceBuffer = new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, - (proto) -> handleOnEntryRemovedFromTrace(proto)); + private final TraceBuffer.ProtoProvider mProtoProvider = + new TraceBuffer.ProtoProvider<MessageNano, + com.android.wm.shell.nano.WmShellTransitionTraceProto, + com.android.wm.shell.nano.Transition>() { + @Override + public int getItemSize(MessageNano proto) { + return proto.getCachedSize(); + } + + @Override + public byte[] getBytes(MessageNano proto) { + return MessageNano.toByteArray(proto); + } + + @Override + public void write( + com.android.wm.shell.nano.WmShellTransitionTraceProto encapsulatingProto, + Queue<com.android.wm.shell.nano.Transition> buffer, OutputStream os) + throws IOException { + encapsulatingProto.transitions = buffer.toArray( + new com.android.wm.shell.nano.Transition[0]); + os.write(getBytes(encapsulatingProto)); + } + }; + private final TraceBuffer<MessageNano, + com.android.wm.shell.nano.WmShellTransitionTraceProto, + com.android.wm.shell.nano.Transition> mTraceBuffer + = new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, mProtoProvider, + (proto) -> handleOnEntryRemovedFromTrace(proto)); private final Map<Object, Runnable> mRemovedFromTraceCallbacks = new HashMap<>(); private final Map<Transitions.TransitionHandler, Integer> mHandlerIds = new HashMap<>(); @@ -78,26 +109,20 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { mHandlerIds.put(handler, handlerId); } - ProtoOutputStream outputStream = new ProtoOutputStream(); - final long protoToken = - outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); - - outputStream.write(com.android.wm.shell.Transition.ID, transitionId); - outputStream.write(com.android.wm.shell.Transition.DISPATCH_TIME_NS, - SystemClock.elapsedRealtimeNanos()); - outputStream.write(com.android.wm.shell.Transition.HANDLER, handlerId); - - outputStream.end(protoToken); + com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition(); + proto.id = transitionId; + proto.dispatchTimeNs = SystemClock.elapsedRealtimeNanos(); + proto.handler = handlerId; final int useCountAfterAdd = mHandlerUseCountInTrace.getOrDefault(handler, 0) + 1; mHandlerUseCountInTrace.put(handler, useCountAfterAdd); - mRemovedFromTraceCallbacks.put(outputStream, () -> { + mRemovedFromTraceCallbacks.put(proto, () -> { final int useCountAfterRemove = mHandlerUseCountInTrace.get(handler) - 1; mHandlerUseCountInTrace.put(handler, useCountAfterRemove); }); - mTraceBuffer.add(outputStream); + mTraceBuffer.add(proto); } /** @@ -107,18 +132,12 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { * @param playingTransitionId The id of the transition we was to merge the transition into. */ public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) { - ProtoOutputStream outputStream = new ProtoOutputStream(); - final long protoToken = - outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); - - outputStream.write(com.android.wm.shell.Transition.ID, mergeRequestedTransitionId); - outputStream.write(com.android.wm.shell.Transition.MERGE_REQUEST_TIME_NS, - SystemClock.elapsedRealtimeNanos()); - outputStream.write(com.android.wm.shell.Transition.MERGED_INTO, playingTransitionId); + com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition(); + proto.id = mergeRequestedTransitionId; + proto.mergeRequestTimeNs = SystemClock.elapsedRealtimeNanos(); + proto.mergedInto = playingTransitionId; - outputStream.end(protoToken); - - mTraceBuffer.add(outputStream); + mTraceBuffer.add(proto); } /** @@ -128,18 +147,12 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { * @param playingTransitionId The id of the transition the transition was merged into. */ public void logMerged(int mergedTransitionId, int playingTransitionId) { - ProtoOutputStream outputStream = new ProtoOutputStream(); - final long protoToken = - outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); - - outputStream.write(com.android.wm.shell.Transition.ID, mergedTransitionId); - outputStream.write( - com.android.wm.shell.Transition.MERGE_TIME_NS, SystemClock.elapsedRealtimeNanos()); - outputStream.write(com.android.wm.shell.Transition.MERGED_INTO, playingTransitionId); + com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition(); + proto.id = mergedTransitionId; + proto.mergeTimeNs = SystemClock.elapsedRealtimeNanos(); + proto.mergedInto = playingTransitionId; - outputStream.end(protoToken); - - mTraceBuffer.add(outputStream); + mTraceBuffer.add(proto); } /** @@ -148,17 +161,11 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { * @param transitionId The id of the transition that was aborted. */ public void logAborted(int transitionId) { - ProtoOutputStream outputStream = new ProtoOutputStream(); - final long protoToken = - outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); - - outputStream.write(com.android.wm.shell.Transition.ID, transitionId); - outputStream.write( - com.android.wm.shell.Transition.ABORT_TIME_NS, SystemClock.elapsedRealtimeNanos()); - - outputStream.end(protoToken); + com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition(); + proto.id = transitionId; + proto.abortTimeNs = SystemClock.elapsedRealtimeNanos(); - mTraceBuffer.add(outputStream); + mTraceBuffer.add(proto); } /** @@ -230,8 +237,9 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) { Trace.beginSection("TransitionTracer#writeTraceToFileLocked"); try { - ProtoOutputStream proto = new ProtoOutputStream(); - proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + com.android.wm.shell.nano.WmShellTransitionTraceProto proto = + new com.android.wm.shell.nano.WmShellTransitionTraceProto(); + proto.magicNumber = MAGIC_NUMBER_VALUE; writeHandlerMappingToProto(proto); int pid = android.os.Process.myPid(); LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath() @@ -243,19 +251,21 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { Trace.endSection(); } - private void writeHandlerMappingToProto(ProtoOutputStream outputStream) { + private void writeHandlerMappingToProto( + com.android.wm.shell.nano.WmShellTransitionTraceProto proto) { + ArrayList<com.android.wm.shell.nano.HandlerMapping> handlerMappings = new ArrayList<>(); for (Transitions.TransitionHandler handler : mHandlerUseCountInTrace.keySet()) { final int count = mHandlerUseCountInTrace.get(handler); if (count > 0) { - final long protoToken = outputStream.start( - com.android.wm.shell.WmShellTransitionTraceProto.HANDLER_MAPPINGS); - outputStream.write(com.android.wm.shell.HandlerMapping.ID, - mHandlerIds.get(handler)); - outputStream.write(com.android.wm.shell.HandlerMapping.NAME, - handler.getClass().getName()); - outputStream.end(protoToken); + com.android.wm.shell.nano.HandlerMapping mapping = + new com.android.wm.shell.nano.HandlerMapping(); + mapping.id = mHandlerIds.get(handler); + mapping.name = handler.getClass().getName(); + handlerMappings.add(mapping); } } + proto.handlerMappings = handlerMappings.toArray( + new com.android.wm.shell.nano.HandlerMapping[0]); } private void handleOnEntryRemovedFromTrace(Object proto) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 12edc3548642..dffbedd197cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -111,7 +111,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private ValueAnimator mDragToDesktopValueAnimator; private final Rect mDragToDesktopAnimationStartBounds = new Rect(); private boolean mDragToDesktopAnimationStarted; - private float mCaptionDragStartX; public DesktopModeWindowDecorViewModel( Context context, @@ -525,7 +524,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration relevantDecor) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: { - mCaptionDragStartX = ev.getX(); // Begin drag through status bar if applicable. if (relevantDecor != null) { mDragToDesktopAnimationStartBounds.set( @@ -580,8 +578,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } if (mTransitionDragActive) { mDesktopTasksController.ifPresent( - c -> c.onDragPositioningMoveThroughStatusBar(relevantDecor.mTaskInfo, - relevantDecor.mTaskSurface, ev.getY())); + c -> c.onDragPositioningMoveThroughStatusBar( + relevantDecor.mTaskInfo, + relevantDecor.mTaskSurface, ev.getY())); final int statusBarHeight = getStatusBarHeight( relevantDecor.mTaskInfo.displayId); if (ev.getY() > statusBarHeight) { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt index 11c5951faa1f..61781565270b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt @@ -18,15 +18,18 @@ package com.android.wm.shell.flicker.appcompat import android.content.Context import android.system.helpers.CommandsHelper +import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.LetterboxAppHelper import android.tools.device.flicker.legacy.FlickerTestFactory import android.tools.device.flicker.legacy.IFlickerTestData -import com.android.server.wm.flicker.helpers.LetterboxAppHelper -import com.android.server.wm.flicker.helpers.setRotation import com.android.wm.shell.flicker.BaseTest -import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.appWindowIsVisibleAtStart +import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd +import com.android.wm.shell.flicker.layerKeepVisible +import org.junit.After import org.junit.Assume import org.junit.Before import org.junit.runners.Parameterized @@ -35,7 +38,7 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { protected val context: Context = instrumentation.context protected val letterboxApp = LetterboxAppHelper(instrumentation) lateinit var cmdHelper: CommandsHelper - lateinit var letterboxStyle: HashMap<String, String> + private lateinit var letterboxStyle: HashMap<String, String> /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -45,12 +48,22 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { letterboxApp.launchViaIntent(wmHelper) setEndRotation() } + teardown { + letterboxApp.exit(wmHelper) + } } @Before fun before() { cmdHelper = CommandsHelper.getInstance(instrumentation) Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest()) + letterboxStyle = mapLetterboxStyle() + setLetterboxEducationEnabled(false) + } + + @After + fun after() { + resetLetterboxEducationEnabled() } private fun mapLetterboxStyle(): HashMap<String, String> { @@ -67,6 +80,22 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { return map } + private fun getLetterboxStyle(): HashMap<String, String> { + if (!::letterboxStyle.isInitialized) { + letterboxStyle = mapLetterboxStyle() + } + return letterboxStyle + } + + private fun resetLetterboxEducationEnabled() { + val enabled = getLetterboxStyle().getValue("Is education enabled") + cmdHelper.executeShellCommand("wm set-letterbox-style --isEducationEnabled $enabled") + } + + private fun setLetterboxEducationEnabled(enabled: Boolean) { + cmdHelper.executeShellCommand("wm set-letterbox-style --isEducationEnabled $enabled") + } + private fun isIgnoreOrientationRequest(): Boolean { val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request") return res != null && res.contains("true") @@ -89,10 +118,7 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { /** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */ private fun assumeLetterboxRoundedCornersEnabled() { - if (!::letterboxStyle.isInitialized) { - letterboxStyle = mapLetterboxStyle() - } - Assume.assumeTrue(letterboxStyle.getValue("Corner radius") != "0") + Assume.assumeTrue(getLetterboxStyle().getValue("Corner radius") != "0") } fun assertLetterboxAppVisibleAtStartAndEnd() { @@ -100,12 +126,20 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { flicker.appWindowIsVisibleAtEnd(letterboxApp) } + fun assertAppLetterboxedAtEnd() = + flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) } + + fun assertAppLetterboxedAtStart() = + flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) } + + fun assertLetterboxAppLayerKeepVisible() = flicker.layerKeepVisible(letterboxApp) + companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation - * modes. + * See [FlickerTestFactory.rotationTests] for configuring screen orientation and + * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt index f212a4e0417e..c2141a370f10 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt @@ -70,6 +70,10 @@ class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) @Test fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners() + @Postsubmit + @Test + fun appIsLetterboxedAtEnd() = assertAppLetterboxedAtEnd() + /** * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't * flicker, and disappears before the transition is complete diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt index 8e75439889b2..b0e1a42306df 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt @@ -53,25 +53,32 @@ class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flick get() = { super.transition(this) transitions { letterboxApp.clickRestart(wmHelper) } - teardown { letterboxApp.exit(wmHelper) } } @Postsubmit @Test fun appVisibleAtStartAndEnd() = assertLetterboxAppVisibleAtStartAndEnd() @Postsubmit @Test - fun appLayerVisibilityChanges() { - flicker.assertLayers { - this.isVisible(letterboxApp) + fun appWindowVisibilityChanges() { + flicker.assertWm { + this.isAppWindowVisible(letterboxApp) .then() - .isInvisible(letterboxApp) + .isAppWindowInvisible(letterboxApp) // animatingExit true .then() - .isVisible(letterboxApp) + .isAppWindowVisible(letterboxApp) // Activity finish relaunching } } @Postsubmit @Test + fun appLayerKeepVisible() = assertLetterboxAppLayerKeepVisible() + + @Postsubmit + @Test + fun appIsLetterboxedAtStart() = assertAppLetterboxedAtStart() + + @Postsubmit + @Test fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtStartHasRoundedCorners() /** Checks that the visible region of [letterboxApp] is still within display bounds */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java index 6199e0b05059..8592dea19289 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java @@ -94,7 +94,7 @@ public class EnterDesktopTaskTransitionHandlerTest { WindowContainerTransaction wct = new WindowContainerTransaction(); doReturn(mToken).when(mTransitions) .startTransition(transitionType, wct, mEnterDesktopTaskTransitionHandler); - mEnterDesktopTaskTransitionHandler.startTransition(transitionType, wct); + mEnterDesktopTaskTransitionHandler.startTransition(transitionType, wct, null); TransitionInfo.Change change = createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM); @@ -115,7 +115,7 @@ public class EnterDesktopTaskTransitionHandlerTest { WindowContainerTransaction wct = new WindowContainerTransaction(); doReturn(mToken).when(mTransitions) .startTransition(transitionType, wct, mEnterDesktopTaskTransitionHandler); - mEnterDesktopTaskTransitionHandler.startTransition(transitionType, wct); + mEnterDesktopTaskTransitionHandler.startTransition(transitionType, wct, null); TransitionInfo.Change change = createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java index 4fad05433d1c..265b10df1945 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java @@ -100,7 +100,7 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase { doReturn(mToken).when(mTransitions) .startTransition(transitionType, wct, mExitDesktopTaskTransitionHandler); - mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct); + mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct, null); TransitionInfo.Change change = createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index 523cb6629d9a..54f36f61859d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -48,6 +48,7 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -71,6 +72,8 @@ public class DragAndDropControllerTest extends ShellTestCase { @Mock private ShellController mShellController; @Mock + private ShellCommandHandler mShellCommandHandler; + @Mock private DisplayController mDisplayController; @Mock private UiEventLogger mUiEventLogger; @@ -89,7 +92,8 @@ public class DragAndDropControllerTest extends ShellTestCase { public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mController = new DragAndDropController(mContext, mShellInit, mShellController, - mDisplayController, mUiEventLogger, mIconProvider, mMainExecutor); + mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider, + mMainExecutor); mController.onInit(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java index 45590951dd1d..9d566860c1cd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java @@ -179,4 +179,23 @@ public class TaskViewTransitionsTest extends ShellTestCase { mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE); assertThat(pendingBounds2).isNull(); } + + @Test + public void testSetTaskVisibility_taskRemoved_noNPE() { + mTaskViewTransitions.removeTaskView(mTaskViewTaskController); + + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, false); + } + + @Test + public void testSetTaskBounds_taskRemoved_noNPE() { + mTaskViewTransitions.removeTaskView(mTaskViewTaskController); + + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, + new Rect(0, 0, 100, 100)); + } } diff --git a/libs/hwui/Tonemapper.cpp b/libs/hwui/Tonemapper.cpp index 0d39f0e33298..974a5d05aa84 100644 --- a/libs/hwui/Tonemapper.cpp +++ b/libs/hwui/Tonemapper.cpp @@ -97,7 +97,6 @@ void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, flo .inputDataspace = sourceDataspace, .outputDataspace = destinationDataspace, .undoPremultipliedAlpha = source.alphaType() == kPremul_SkAlphaType, - .fakeInputDataspace = destinationDataspace, .type = shaders::LinearEffect::SkSLType::ColorFilter}; constexpr float kMaxDisplayBrightnessNits = 1000.f; constexpr float kCurrentDisplayBrightnessNits = 500.f; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 7e9d44fbdbd1..c00a2707e0a2 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -29,6 +29,7 @@ #include "Layer.h" #include "Properties.h" #include "RenderThread.h" +#include "VulkanManager.h" #include "pipeline/skia/ATraceMemoryDump.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" @@ -182,8 +183,14 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) } log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts); + auto vkInstance = VulkanManager::peekInstance(); if (!mGrContext) { - log.appendFormat("No GPU context.\n"); + if (!vkInstance) { + log.appendFormat("No GPU context.\n"); + } else { + log.appendFormat("No GrContext; however %d remaining Vulkan refs", + vkInstance->getStrongCount() - 1); + } return; } std::vector<skiapipeline::ResourcePair> cpuResourceMap = { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index f198bca9060c..4cffc6c2efe3 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -107,11 +107,11 @@ GrVkGetProc VulkanManager::sSkiaGetProp = [](const char* proc_name, VkInstance i #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) -sp<VulkanManager> VulkanManager::getInstance() { - // cache a weakptr to the context to enable a second thread to share the same vulkan state - static wp<VulkanManager> sWeakInstance = nullptr; - static std::mutex sLock; +// cache a weakptr to the context to enable a second thread to share the same vulkan state +static wp<VulkanManager> sWeakInstance = nullptr; +static std::mutex sLock; +sp<VulkanManager> VulkanManager::getInstance() { std::lock_guard _lock{sLock}; sp<VulkanManager> vulkanManager = sWeakInstance.promote(); if (!vulkanManager.get()) { @@ -122,6 +122,11 @@ sp<VulkanManager> VulkanManager::getInstance() { return vulkanManager; } +sp<VulkanManager> VulkanManager::peekInstance() { + std::lock_guard _lock{sLock}; + return sWeakInstance.promote(); +} + VulkanManager::~VulkanManager() { if (mDevice != VK_NULL_HANDLE) { mDeviceWaitIdle(mDevice); @@ -404,9 +409,13 @@ void VulkanManager::initialize() { } } -sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options, - ContextType contextType) { +static void onGrContextReleased(void* context) { + VulkanManager* manager = (VulkanManager*)context; + manager->decStrong((void*)onGrContextReleased); +} +sk_sp<GrDirectContext> VulkanManager::createContext(GrContextOptions& options, + ContextType contextType) { GrVkBackendContext backendContext; backendContext.fInstance = mInstance; backendContext.fPhysicalDevice = mPhysicalDevice; @@ -418,6 +427,11 @@ sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& opti backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; backendContext.fGetProc = sSkiaGetProp; + LOG_ALWAYS_FATAL_IF(options.fContextDeleteProc != nullptr, "Conflicting fContextDeleteProcs!"); + this->incStrong((void*)onGrContextReleased); + options.fContextDeleteContext = this; + options.fContextDeleteProc = onGrContextReleased; + return GrDirectContext::MakeVulkan(backendContext, options); } diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index c5196eeccea3..00a40c0c85c3 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -66,6 +66,7 @@ class RenderThread; class VulkanManager final : public RefBase { public: static sp<VulkanManager> getInstance(); + static sp<VulkanManager> peekInstance(); // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must // be call once before use of the VulkanManager. Multiple calls after the first will simiply @@ -109,7 +110,7 @@ public: }; // returns a Skia graphic context used to draw content on the specified thread - sk_sp<GrDirectContext> createContext(const GrContextOptions& options, + sk_sp<GrDirectContext> createContext(GrContextOptions& options, ContextType contextType = ContextType::kRenderThread); uint32_t getDriverVersion() const { return mDriverVersion; } diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index bffe137c2bd3..913af8ac3474 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -284,7 +284,9 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { case HAL_DATASPACE_TRANSFER_GAMMA2_8: return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_ST2084: - return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut); + return SkColorSpace::MakeRGB({-2.0, -1.555223, 1.860454, 32 / 2523.0, 2413 / 128.0, + -2392 / 128.0, 8192 / 1305.0}, + gamut); case HAL_DATASPACE_TRANSFER_SMPTE_170M: return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut); case HAL_DATASPACE_TRANSFER_UNSPECIFIED: @@ -427,10 +429,10 @@ skcms_TransferFunction GetExtendedTransferFunction(float sdrHdrRatio) { } // Skia skcms' default HLG maps encoded [0, 1] to linear [1, 12] in order to follow ARIB -// but LinearEffect expects a decoded [0, 1] range instead to follow Rec 2100. +// but LinearEffect expects to map 1.0 == 203 nits std::optional<skcms_TransferFunction> GetHLGScaleTransferFunction() { skcms_TransferFunction hlgFn; - if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 1.f / 12.f, 2.f, 2.f, 1.f / 0.17883277f, + if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 0.314509843, 2.f, 2.f, 1.f / 0.17883277f, 0.28466892f, 0.55991073f)) { return std::make_optional<skcms_TransferFunction>(hlgFn); } diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl index 6092ac53ec39..82fc33edf08a 100644 --- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl @@ -15,8 +15,8 @@ */ package android.media.soundtrigger_middleware; -import android.media.soundtrigger.RecognitionEvent; -import android.media.soundtrigger.PhraseRecognitionEvent; +import android.media.soundtrigger_middleware.RecognitionEventSys; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; /** * Main interface for a client to get notifications of events coming from this module. @@ -31,7 +31,7 @@ oneway interface ISoundTriggerCallback { * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange() * callback. */ - void onRecognition(int modelHandle, in RecognitionEvent event, int captureSession); + void onRecognition(int modelHandle, in RecognitionEventSys event, int captureSession); /** * Invoked whenever a phrase recognition event is triggered (typically, on recognition, but * also in case of external aborting of a recognition or a forced recognition event - see the @@ -39,7 +39,7 @@ oneway interface ISoundTriggerCallback { * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange() * callback. */ - void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event, int captureSession); + void onPhraseRecognition(int modelHandle, in PhraseRecognitionEventSys event, int captureSession); /** * Notifies the client that some start/load operations that have previously failed for resource * reasons (threw a ServiceSpecificException(RESOURCE_CONTENTION) or have been preempted) may diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEventSys.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEventSys.aidl new file mode 100644 index 000000000000..6c912ed7e056 --- /dev/null +++ b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEventSys.aidl @@ -0,0 +1,36 @@ +/* + * 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.media.soundtrigger_middleware; + +import android.media.soundtrigger.PhraseRecognitionEvent; + +/** + * Wrapper to android.media.soundtrigger.RecognitionEvent providing additional fields used by the + * framework. + */ +parcelable PhraseRecognitionEventSys { + + PhraseRecognitionEvent phraseRecognitionEvent; + /** + * Timestamp of when the trigger event from SoundTriggerHal was received by the + * framework. + * + * <p>same units and timebase as {@link SystemClock#elapsedRealtime()}. + * The value will be -1 if the event was not generated from the HAL. + */ + // @ElapsedRealtimeLong + long halEventReceivedMillis = -1; +} diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionEventSys.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionEventSys.aidl new file mode 100644 index 000000000000..84e327d5df8c --- /dev/null +++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionEventSys.aidl @@ -0,0 +1,36 @@ +/* + * 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.media.soundtrigger_middleware; + +import android.media.soundtrigger.RecognitionEvent; + +/** + * Wrapper to android.media.soundtrigger.RecognitionEvent providing additional fields used by the + * framework. + */ +parcelable RecognitionEventSys { + + RecognitionEvent recognitionEvent; + /** + * Timestamp of when the trigger event from SoundTriggerHal was received by the + * framework. + * + * <p>same units and timebase as {@link SystemClock#elapsedRealtime()}. + * The value will be -1 if the event was not generated from the HAL. + */ + // @ElapsedRealtimeLong + long halEventReceivedMillis = -1; +} diff --git a/native/android/activity_manager.cpp b/native/android/activity_manager.cpp index 155a355241c8..bc6a84f01517 100644 --- a/native/android/activity_manager.cpp +++ b/native/android/activity_manager.cpp @@ -45,7 +45,7 @@ struct UidObserver : public BnUidObserver, public virtual IBinder::DeathRecipien void onUidIdle(uid_t uid, bool disabled) override; void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq, int32_t capability) override; - void onUidProcAdjChanged(uid_t uid) override; + void onUidProcAdjChanged(uid_t uid, int32_t adj) override; // IBinder::DeathRecipient implementation void binderDied(const wp<IBinder>& who) override; @@ -121,7 +121,7 @@ void UidObserver::onUidActive(uid_t uid __unused) {} void UidObserver::onUidIdle(uid_t uid __unused, bool disabled __unused) {} -void UidObserver::onUidProcAdjChanged(uid_t uid __unused) {} +void UidObserver::onUidProcAdjChanged(uid_t uid __unused, int32_t adj __unused) {} void UidObserver::onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq __unused, diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java index 5f067e9836a2..b888739016c7 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java @@ -60,6 +60,7 @@ public class SlicePurchaseActivity extends Activity { @NonNull private Intent mIntent; @NonNull private URL mUrl; @TelephonyManager.PremiumCapability protected int mCapability; + private boolean mIsUserTriggeredFinish; @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,6 +72,7 @@ public class SlicePurchaseActivity extends Activity { SlicePurchaseController.PREMIUM_CAPABILITY_INVALID); String url = mIntent.getStringExtra(SlicePurchaseController.EXTRA_PURCHASE_URL); mApplicationContext = getApplicationContext(); + mIsUserTriggeredFinish = true; logd("onCreate: subId=" + subId + ", capability=" + TelephonyManager.convertPremiumCapabilityToString(mCapability) + ", url=" + url); @@ -153,12 +155,20 @@ public class SlicePurchaseActivity extends Activity { @Override protected void onDestroy() { - logd("onDestroy: User canceled the purchase by closing the application."); - SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse( - mIntent, SlicePurchaseController.EXTRA_INTENT_CANCELED); + if (mIsUserTriggeredFinish) { + logd("onDestroy: User canceled the purchase by closing the application."); + SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse( + mIntent, SlicePurchaseController.EXTRA_INTENT_CANCELED); + } super.onDestroy(); } + @Override + public void finishAndRemoveTask() { + mIsUserTriggeredFinish = false; + super.finishAndRemoveTask(); + } + private void setupWebView() { // Create WebView mWebView = new WebView(this); diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java index f594bf270d29..7ed18163dd83 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java @@ -123,12 +123,11 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more); } - setAccessibility(view, viewType, - AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand, 0); - // Add expand buttons if the permissions are more than PERMISSION_SIZE in this list also // make the summary invisible by default. if (mPermissions.size() > PERMISSION_SIZE) { + setAccessibility(view, viewType, + AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand, 0); viewHolder.mPermissionSummary.setVisibility(View.GONE); diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 7d71bd5c91ab..6df07786f514 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -24,8 +24,8 @@ <string name="string_cancel">Cancel</string> <!-- This is a label for a button that takes user to the next screen. [CHAR LIMIT=20] --> <string name="string_continue">Continue</string> - <!-- This is a label for a button that links to different places where the user can save their passkeys. [CHAR LIMIT=20] --> - <string name="string_more_options">More options</string> + <!-- This is a label for a button that leads to a holistic view of all different options where the user can save their new app credential. [CHAR LIMIT=20] --> + <string name="string_more_options">Save another way</string> <!-- This is a label for a button that links to additional information about passkeys. [CHAR LIMIT=20] --> <string name="string_learn_more">Learn more</string> <!-- This is a label for content description for show password icon button. --> @@ -120,6 +120,8 @@ <!-- Strings for the get flow. --> <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] --> <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> + <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved password to sign in to the app. [CHAR LIMIT=200] --> + <string name="get_dialog_title_use_password_for">Use your saved password for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> <!-- This appears as the title of the dialog asking for user confirmation to use the single user credential (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] --> <string name="get_dialog_title_use_sign_in_for">Use your sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> <!-- This appears as the title of the dialog asking for user to make a choice from various available user credentials (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] --> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 1fb5e3f853b2..54a8678df9cc 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -25,6 +25,7 @@ import android.os.Bundle import android.os.ResultReceiver import android.util.Log import androidx.activity.ComponentActivity +import androidx.activity.OnBackPressedCallback import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.viewModels @@ -48,11 +49,12 @@ import com.android.credentialmanager.ui.theme.PlatformTheme class CredentialSelectorActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity") overrideActivityTransition(Activity.OVERRIDE_TRANSITION_OPEN, 0, 0) overrideActivityTransition(Activity.OVERRIDE_TRANSITION_CLOSE, 0, 0) - Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity") + try { val (isCancellationRequest, shouldShowCancellationUi, _) = maybeCancelUIUponRequest(intent) @@ -61,6 +63,18 @@ class CredentialSelectorActivity : ComponentActivity() { } val userConfigRepo = UserConfigRepo(this) val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo) + + val backPressedCallback = object : OnBackPressedCallback( + true // default to enabled + ) { + override fun handleOnBackPressed() { + credManRepo.onUserCancel() + Log.d(Constants.LOG_TAG, "Activity back triggered: finish the activity.") + this@CredentialSelectorActivity.finish() + } + } + onBackPressedDispatcher.addCallback(this, backPressedCallback) + setContent { PlatformTheme { CredentialManagerBottomSheet( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 00c2f1a63ed4..a35310cdad95 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -482,7 +482,8 @@ class CreateFlowUtils { CredentialType.PASSWORD, appLabel, context.getDrawable(R.drawable.ic_password_24) ?: return null, - preferImmediatelyAvailableCredentials = false, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, appPreferredDefaultProviderId = appPreferredDefaultProviderId, userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(), ) @@ -509,7 +510,8 @@ class CreateFlowUtils { appName = appLabel, typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context) ?: context.getDrawable(R.drawable.ic_other_sign_in_24) ?: return null, - preferImmediatelyAvailableCredentials = false, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, appPreferredDefaultProviderId = appPreferredDefaultProviderId, userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(), ) diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 706666cbebab..2e49dd5eeba9 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -841,7 +841,8 @@ public class SettingsBackupTest { Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, Settings.Secure.UI_TRANSLATION_ENABLED, - Settings.Secure.CREDENTIAL_SERVICE); + Settings.Secure.CREDENTIAL_SERVICE, + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY); @Test public void systemSettingsBackedUpOrDenied() { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index a110f56d09bd..8b3fd41b62a6 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -17,12 +17,12 @@ */ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.shell" - coreApp="true" - android:sharedUserId="android.uid.shell" - > + package="com.android.shell" + coreApp="true" + android:sharedUserId="android.uid.shell" + > - <!-- Standard permissions granted to the shell. --> + <!-- Standard permissions granted to the shell. --> <uses-permission android:name="android.permission.MANAGE_HEALTH_PERMISSIONS" /> <uses-permission android:name="android.permission.MANAGE_HEALTH_DATA" /> <uses-permission android:name="android.permission.health.READ_EXERCISE_ROUTE" /> @@ -125,7 +125,7 @@ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> - <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/> + <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- BLUETOOTH_PRIVILEGED is needed for testing purposes only. --> <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /> @@ -136,7 +136,7 @@ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.MANAGE_USB" /> <uses-permission android:name="android.permission.USE_RESERVED_DISK" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- System tool permissions granted to the shell. --> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> @@ -231,16 +231,16 @@ <uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" /> <uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" /> <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" /> - <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/> - <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/> - <uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/> + <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE" /> + <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE" /> + <uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL" /> <uses-permission android:name="android.permission.BLUETOOTH_STACK" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" /> <uses-permission android:name="android.permission.FRAME_STATS" /> <uses-permission android:name="android.permission.BIND_APPWIDGET" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> - <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/> + <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS" /> <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" /> <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> @@ -304,7 +304,7 @@ <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.BACKGROUND_CAMERA" /> <uses-permission android:name="android.permission.SYSTEM_CAMERA" /> - <!-- Permissions needed to test onCameraOpened/Closed callbacks --> + <!-- Permissions needed to test onCameraOpened/Closed callbacks --> <uses-permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" /> <!-- Permissions needed for CTS camera test: RecordingTest.java when assuming shell id --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> @@ -344,7 +344,7 @@ <uses-permission android:name="android.permission.LOADER_USAGE_STATS" /> <!-- Permission required for storage tests - FuseDaemonHostTest --> - <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- Permission needed to run network tests in CTS --> <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" /> @@ -386,54 +386,54 @@ <uses-permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE" /> <!-- Permission required for CTS test - CrossProfileAppsHostSideTest --> - <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES" /> <!-- Permission required for CTS test - CrossProfileAppsHostSideTest --> - <uses-permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"/> + <uses-permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES" /> <!-- permissions required for CTS test - PhoneStateListenerTest --> <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" /> <!-- Permissions required for granting and logging --> - <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> - <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> - <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/> - <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" /> + <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD" /> <!-- Permission required for CTS test - BatterySaverTest --> - <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/> + <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" /> <!-- Permission required for CTS test - UiModeManagerTest --> - <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/> - <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/> + <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED" /> + <uses-permission android:name="android.permission.READ_PROJECTION_STATE" /> <!-- Permission required for CTS tests - UiModeManagerTest, CarModeInCallServiceTest --> - <uses-permission android:name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION"/> + <uses-permission android:name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION" /> <!-- Permission required for CTS test - SystemConfigTest --> - <uses-permission android:name="android.permission.READ_CARRIER_APP_INFO"/> + <uses-permission android:name="android.permission.READ_CARRIER_APP_INFO" /> <!-- Permission required for CTS test - CarModeInCallServiceTest --> - <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/> + <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" /> <!-- Permission requried for CTS test - CellBroadcastIntentsTest --> - <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS"/> + <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" /> <!-- Permission required for CTS test - TetheringManagerTest --> - <uses-permission android:name="android.permission.TETHER_PRIVILEGED"/> + <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> <!-- Permission required for CTS test - CtsOsTestCases --> - <uses-permission android:name="android.permission.MANAGE_CRATES"/> + <uses-permission android:name="android.permission.MANAGE_CRATES" /> <!-- Allows setting brightness from the shell --> - <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/> + <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" /> <!-- Permission required for CTS test - ShortcutManagerUsageTest --> - <uses-permission android:name="android.permission.ACCESS_SHORTCUTS"/> + <uses-permission android:name="android.permission.ACCESS_SHORTCUTS" /> <!-- Permissions required for CTS test - UsageStatsTest --> - <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS"/> - <uses-permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"/> + <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS" /> <!-- Permission needed for CTS test - MusicRecognitionManagerTest --> <uses-permission android:name="android.permission.MANAGE_MUSIC_RECOGNITION" /> @@ -442,8 +442,8 @@ <uses-permission android:name="android.permission.MANAGE_SPEECH_RECOGNITION" /> <!-- Permissions required to test ambient display. --> - <uses-permission android:name="android.permission.READ_DREAM_STATE"/> - <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/> + <uses-permission android:name="android.permission.READ_DREAM_STATE" /> + <uses-permission android:name="android.permission.WRITE_DREAM_STATE" /> <!-- Permission required for CTS test - CtsLightsManagerTest --> <uses-permission android:name="android.permission.CONTROL_DEVICE_LIGHTS" /> @@ -470,7 +470,7 @@ <uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" /> <!-- Permission required for testing system audio effect APIs. --> - <uses-permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/> + <uses-permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS" /> <!-- Permission required for running networking unit tests --> <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" /> @@ -495,7 +495,7 @@ <uses-permission android:name="android.permission.TV_INPUT_HARDWARE" /> <uses-permission android:name="android.permission.TIS_EXTENSION_INTERFACE" /> <uses-permission android:name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS" /> - <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/> + <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <!-- Permission needed for CTS test - PrivilegedLocationPermissionTest --> <uses-permission android:name="android.permission.LOCATION_HARDWARE" /> @@ -560,14 +560,14 @@ <uses-permission android:name="android.permission.BIND_CARRIER_SERVICES" /> <!-- Allows overriding the system's device state from the shell --> - <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/> + <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE" /> <!-- Permissions required for CTS tests to close system dialogs --> <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" /> <!-- Permissions required for CTS test - HideOverlayWindowsTest --> - <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> - <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY"/> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" /> <!-- Permission required for CTS test - CtsHdmiCecHostTestCases --> <uses-permission android:name="android.permission.HDMI_CEC" /> @@ -630,21 +630,21 @@ <uses-permission android:name="android.permission.UPDATE_FONTS" /> <!-- Permission required for Launcher testing - DigitalWellbeingToastTest --> - <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"/> + <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" /> <!-- Permission required for hotword detection service CTS tests --> <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" /> <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" /> <!-- Permission required for CTS test - CtsVoiceInteractionTestCases --> - <uses-permission android:name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"/> + <uses-permission android:name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER" /> <uses-permission android:name="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE" /> <!-- Permission required for CTS test - KeyguardLockedStateApiTest --> <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" /> - <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/> + <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" /> <!-- Permission required for CTS test - MediaCodecResourceTest --> <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" /> @@ -820,8 +820,8 @@ <uses-permission android:name="android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA" /> <uses-permission android:name="android.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" /> <!-- Permissions required for CTS test - CtsBroadcastRadioTestCases --> <uses-permission android:name="android.permission.ACCESS_BROADCAST_RADIO" /> @@ -832,12 +832,17 @@ <!-- Permission required for CTS test - CtsTelephonyProviderTestCases --> <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" /> - <uses-permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE"/> - - <application android:label="@string/app_label" - android:theme="@android:style/Theme.DeviceDefault.DayNight" - android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true"> + <uses-permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE" /> + <!-- Permission required for GTS test - GtsAttestationVerificationDeviceSideTestCases --> + <uses-permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" /> + <!-- Permission required for GTS test - GtsCredentialsTestCases --> + <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" /> + + <application + android:label="@string/app_label" + android:theme="@android:style/Theme.DeviceDefault.DayNight" + android:defaultToDeviceProtectedStorage="true" + android:directBootAware="true"> <provider android:name="androidx.core.content.FileProvider" android:authorities="com.android.shell" @@ -860,10 +865,11 @@ </intent-filter> </provider> - <provider android:name=".HeapDumpProvider" - android:authorities="com.android.shell.heapdump" - android:grantUriPermissions="true" - android:exported="false" /> + <provider + android:name=".HeapDumpProvider" + android:authorities="com.android.shell.heapdump" + android:grantUriPermissions="true" + android:exported="false" /> <activity android:name=".BugreportWarningActivity" @@ -872,13 +878,14 @@ android:excludeFromRecents="true" android:exported="false" /> - <activity android:name=".HeapDumpActivity" - android:theme="@*android:style/Theme.Translucent.NoTitleBar" - android:label="@*android:string/dump_heap_title" - android:finishOnCloseSystemDialogs="true" - android:noHistory="true" - android:excludeFromRecents="true" - android:exported="false" /> + <activity + android:name=".HeapDumpActivity" + android:theme="@*android:style/Theme.Translucent.NoTitleBar" + android:label="@*android:string/dump_heap_title" + android:finishOnCloseSystemDialogs="true" + android:noHistory="true" + android:excludeFromRecents="true" + android:exported="false" /> <receiver android:name=".BugreportRequestedReceiver" @@ -903,7 +910,7 @@ <receiver android:name=".ProfcollectUploadReceiver" android:exported="true" - android:permission="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD" > + android:permission="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD"> <intent-filter> <action android:name="com.android.shell.action.PROFCOLLECT_UPLOAD" /> </intent-filter> @@ -912,6 +919,6 @@ <service android:name=".BugreportProgressService" android:foregroundServiceType="systemExempted" - android:exported="false"/> + android:exported="false" /> </application> </manifest> diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp index 5b5871f95fb3..8eb012d2e41c 100644 --- a/packages/SystemUI/animation/Android.bp +++ b/packages/SystemUI/animation/Android.bp @@ -43,6 +43,7 @@ android_library { "androidx.core_core-ktx", "androidx.annotation_annotation", "SystemUIShaderLib", + "animationlib", ], manifest: "AndroidManifest.xml", diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 94b37403b232..4037fd4a09d8 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -39,9 +39,9 @@ import android.view.animation.Interpolator import android.view.animation.PathInterpolator import androidx.annotation.BinderThread import androidx.annotation.UiThread +import com.android.app.animation.Interpolators import com.android.internal.annotations.VisibleForTesting import com.android.internal.policy.ScreenDecorationsUtils -import java.lang.IllegalArgumentException import kotlin.math.roundToInt private const val TAG = "ActivityLaunchAnimator" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index 42a86363bf01..48dd08f206c1 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -33,10 +33,10 @@ import android.view.WindowInsets import android.view.WindowManager import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS import android.widget.FrameLayout +import com.android.app.animation.Interpolators import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CujType import com.android.systemui.util.registerAnimationOnBackInvoked -import java.lang.IllegalArgumentException import kotlin.math.roundToInt private const val TAG = "DialogLaunchAnimator" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java deleted file mode 100644 index 9dbb9205b90e..000000000000 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2016 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.animation; - -import android.graphics.Path; -import android.util.MathUtils; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.BounceInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; -import android.view.animation.PathInterpolator; - -/** - * Utility class to receive interpolators from. - * - * Make sure that changes made to this class are also reflected in {@link InterpolatorsAndroidX}. - * Please consider using the androidx dependencies featuring better testability altogether. - */ -public class Interpolators { - - /* - * ============================================================================================ - * Emphasized interpolators. - * ============================================================================================ - */ - - /** - * The default emphasized interpolator. Used for hero / emphasized movement of content. - */ - public static final Interpolator EMPHASIZED = createEmphasizedInterpolator(); - - /** - * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that - * is disappearing e.g. when moving off screen. - */ - public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator( - 0.3f, 0f, 0.8f, 0.15f); - - /** - * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that - * is appearing e.g. when coming from off screen - */ - public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator( - 0.05f, 0.7f, 0.1f, 1f); - - - /* - * ============================================================================================ - * Standard interpolators. - * ============================================================================================ - */ - - /** - * The standard interpolator that should be used on every normal animation - */ - public static final Interpolator STANDARD = new PathInterpolator( - 0.2f, 0f, 0f, 1f); - - /** - * The standard accelerating interpolator that should be used on every regular movement of - * content that is disappearing e.g. when moving off screen. - */ - public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator( - 0.3f, 0f, 1f, 1f); - - /** - * The standard decelerating interpolator that should be used on every regular movement of - * content that is appearing e.g. when coming from off screen. - */ - public static final Interpolator STANDARD_DECELERATE = new PathInterpolator( - 0f, 0f, 0f, 1f); - - /* - * ============================================================================================ - * Legacy - * ============================================================================================ - */ - - /** - * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN. - */ - public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f); - - /** - * The default legacy accelerating interpolator as defined in Material 1. - * Also known as FAST_OUT_LINEAR_IN. - */ - public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f); - - /** - * The default legacy decelerating interpolator as defined in Material 1. - * Also known as LINEAR_OUT_SLOW_IN. - */ - public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f); - - /** - * Linear interpolator. Often used if the interpolator is for different properties who need - * different interpolations. - */ - public static final Interpolator LINEAR = new LinearInterpolator(); - - /* - * ============================================================================================ - * Custom interpolators - * ============================================================================================ - */ - - public static final Interpolator FAST_OUT_SLOW_IN = LEGACY; - public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE; - public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE; - - /** - * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t - * goes from 1 to 0 instead of 0 to 1). - */ - public static final Interpolator FAST_OUT_SLOW_IN_REVERSE = - new PathInterpolator(0.8f, 0f, 0.6f, 1f); - public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f); - public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); - public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); - public static final Interpolator ACCELERATE = new AccelerateInterpolator(); - public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator(); - public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); - public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f); - public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f); - public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f, - 1.1f); - public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f, - 1); - public static final Interpolator BOUNCE = new BounceInterpolator(); - /** - * For state transitions on the control panel that lives in GlobalActions. - */ - public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f, - 1.0f); - - /** - * Interpolator to be used when animating a move based on a click. Pair with enough duration. - */ - public static final Interpolator TOUCH_RESPONSE = - new PathInterpolator(0.3f, 0f, 0.1f, 1f); - - /** - * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t - * goes from 1 to 0 instead of 0 to 1). - */ - public static final Interpolator TOUCH_RESPONSE_REVERSE = - new PathInterpolator(0.9f, 0f, 0.7f, 1f); - - /* - * ============================================================================================ - * Functions / Utilities - * ============================================================================================ - */ - - /** - * Calculate the amount of overshoot using an exponential falloff function with desired - * properties, where the overshoot smoothly transitions at the 1.0f boundary into the - * overshoot, retaining its acceleration. - * - * @param progress a progress value going from 0 to 1 - * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max - * value of the overall progress will be at 1.1. - * @param overshootStart the point in (0,1] where the result should reach 1 - * @return the interpolated overshoot - */ - public static float getOvershootInterpolation(float progress, float overshootAmount, - float overshootStart) { - if (overshootAmount == 0.0f || overshootStart == 0.0f) { - throw new IllegalArgumentException("Invalid values for overshoot"); - } - float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart; - return MathUtils.max(0.0f, - (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f)); - } - - /** - * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot - * starts immediately here, instead of first having a section of non-overshooting - * - * @param progress a progress value going from 0 to 1 - */ - public static float getOvershootInterpolation(float progress) { - return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress))); - } - - // Create the default emphasized interpolator - private static PathInterpolator createEmphasizedInterpolator() { - Path path = new Path(); - // Doing the same as fast_out_extra_slow_in - path.moveTo(0f, 0f); - path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f); - path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f); - return new PathInterpolator(path); - } -} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java b/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java deleted file mode 100644 index 8da87feb1fee..000000000000 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.animation; - -import android.graphics.Path; -import android.util.MathUtils; - -import androidx.core.animation.AccelerateDecelerateInterpolator; -import androidx.core.animation.AccelerateInterpolator; -import androidx.core.animation.BounceInterpolator; -import androidx.core.animation.DecelerateInterpolator; -import androidx.core.animation.Interpolator; -import androidx.core.animation.LinearInterpolator; -import androidx.core.animation.PathInterpolator; - -/** - * Utility class to receive interpolators from. (androidx compatible version) - * - * This is the androidx compatible version of {@link Interpolators}. Make sure that changes made to - * this class are also reflected in {@link Interpolators}. - * - * Using the androidx versions of {@link androidx.core.animation.ValueAnimator} or - * {@link androidx.core.animation.ObjectAnimator} improves animation testability. This file provides - * the androidx compatible versions of the interpolators defined in {@link Interpolators}. - * AnimatorTestRule can be used in Tests to manipulate the animation under test (e.g. artificially - * advancing the time). - */ -public class InterpolatorsAndroidX { - - /* - * ============================================================================================ - * Emphasized interpolators. - * ============================================================================================ - */ - - /** - * The default emphasized interpolator. Used for hero / emphasized movement of content. - */ - public static final Interpolator EMPHASIZED = createEmphasizedInterpolator(); - - /** - * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that - * is disappearing e.g. when moving off screen. - */ - public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator( - 0.3f, 0f, 0.8f, 0.15f); - - /** - * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that - * is appearing e.g. when coming from off screen - */ - public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator( - 0.05f, 0.7f, 0.1f, 1f); - - - /* - * ============================================================================================ - * Standard interpolators. - * ============================================================================================ - */ - - /** - * The standard interpolator that should be used on every normal animation - */ - public static final Interpolator STANDARD = new PathInterpolator( - 0.2f, 0f, 0f, 1f); - - /** - * The standard accelerating interpolator that should be used on every regular movement of - * content that is disappearing e.g. when moving off screen. - */ - public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator( - 0.3f, 0f, 1f, 1f); - - /** - * The standard decelerating interpolator that should be used on every regular movement of - * content that is appearing e.g. when coming from off screen. - */ - public static final Interpolator STANDARD_DECELERATE = new PathInterpolator( - 0f, 0f, 0f, 1f); - - /* - * ============================================================================================ - * Legacy - * ============================================================================================ - */ - - /** - * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN. - */ - public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f); - - /** - * The default legacy accelerating interpolator as defined in Material 1. - * Also known as FAST_OUT_LINEAR_IN. - */ - public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f); - - /** - * The default legacy decelerating interpolator as defined in Material 1. - * Also known as LINEAR_OUT_SLOW_IN. - */ - public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f); - - /** - * Linear interpolator. Often used if the interpolator is for different properties who need - * different interpolations. - */ - public static final Interpolator LINEAR = new LinearInterpolator(); - - /* - * ============================================================================================ - * Custom interpolators - * ============================================================================================ - */ - - public static final Interpolator FAST_OUT_SLOW_IN = LEGACY; - public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE; - public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE; - - /** - * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t - * goes from 1 to 0 instead of 0 to 1). - */ - public static final Interpolator FAST_OUT_SLOW_IN_REVERSE = - new PathInterpolator(0.8f, 0f, 0.6f, 1f); - public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f); - public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); - public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); - public static final Interpolator ACCELERATE = new AccelerateInterpolator(); - public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator(); - public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); - public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f); - public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f); - public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f, - 1.1f); - public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f, - 1); - public static final Interpolator BOUNCE = new BounceInterpolator(); - /** - * For state transitions on the control panel that lives in GlobalActions. - */ - public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f, - 1.0f); - - /** - * Interpolator to be used when animating a move based on a click. Pair with enough duration. - */ - public static final Interpolator TOUCH_RESPONSE = - new PathInterpolator(0.3f, 0f, 0.1f, 1f); - - /** - * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t - * goes from 1 to 0 instead of 0 to 1). - */ - public static final Interpolator TOUCH_RESPONSE_REVERSE = - new PathInterpolator(0.9f, 0f, 0.7f, 1f); - - /* - * ============================================================================================ - * Functions / Utilities - * ============================================================================================ - */ - - /** - * Calculate the amount of overshoot using an exponential falloff function with desired - * properties, where the overshoot smoothly transitions at the 1.0f boundary into the - * overshoot, retaining its acceleration. - * - * @param progress a progress value going from 0 to 1 - * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max - * value of the overall progress will be at 1.1. - * @param overshootStart the point in (0,1] where the result should reach 1 - * @return the interpolated overshoot - */ - public static float getOvershootInterpolation(float progress, float overshootAmount, - float overshootStart) { - if (overshootAmount == 0.0f || overshootStart == 0.0f) { - throw new IllegalArgumentException("Invalid values for overshoot"); - } - float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart; - return MathUtils.max(0.0f, - (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f)); - } - - /** - * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot - * starts immediately here, instead of first having a section of non-overshooting - * - * @param progress a progress value going from 0 to 1 - */ - public static float getOvershootInterpolation(float progress) { - return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress))); - } - - // Create the default emphasized interpolator - private static PathInterpolator createEmphasizedInterpolator() { - Path path = new Path(); - // Doing the same as fast_out_extra_slow_in - path.moveTo(0f, 0f); - path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f); - path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f); - return new PathInterpolator(path); - } -} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt index 3417ffd6b83a..142fd21d4a16 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -28,7 +28,7 @@ import android.util.MathUtils import android.view.View import android.view.ViewGroup import android.view.animation.Interpolator -import com.android.systemui.animation.Interpolators.LINEAR +import com.android.app.animation.Interpolators.LINEAR import kotlin.math.roundToInt private const val TAG = "LaunchAnimator" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index 58ffef25cb42..8e79e3ce1742 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -25,6 +25,7 @@ import android.util.IntProperty import android.view.View import android.view.ViewGroup import android.view.animation.Interpolator +import com.android.app.animation.Interpolators import kotlin.math.max import kotlin.math.min diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt index f3d8b1782486..dd32851a97cf 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt @@ -19,7 +19,7 @@ package com.android.systemui.animation.back import android.util.DisplayMetrics import android.view.animation.Interpolator import android.window.BackEvent -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.util.dpToPx /** Used to convert [BackEvent] into a [BackTransformation]. */ diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index 941a925c9d10..8dd2c39e7a58 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -28,9 +28,9 @@ import android.text.format.DateFormat import android.util.AttributeSet import android.util.MathUtils.constrainedMap import android.widget.TextView +import com.android.app.animation.Interpolators import com.android.internal.annotations.VisibleForTesting import com.android.systemui.animation.GlyphCallback -import com.android.systemui.animation.Interpolators import com.android.systemui.animation.TextAnimator import com.android.systemui.customization.R import com.android.systemui.log.LogBuffer diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt index 08ee60204178..6f363a4ffa26 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt @@ -20,6 +20,8 @@ package com.android.systemui.shared.quickaffordance.shared.model object KeyguardPreviewConstants { const val MESSAGE_ID_HIDE_SMART_SPACE = 1111 const val KEY_HIDE_SMART_SPACE = "hide_smart_space" + const val MESSAGE_ID_COLOR_OVERRIDE = 1234 + const val KEY_COLOR_OVERRIDE = "color_override" // ColorInt Encoded as string const val MESSAGE_ID_SLOT_SELECTED = 1337 const val KEY_SLOT_ID = "slot_id" const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id" diff --git a/packages/SystemUI/res/drawable/ic_keyboard_backlight.xml b/packages/SystemUI/res/drawable/ic_keyboard_backlight.xml index d123caf82ed5..cff2839e6f88 100644 --- a/packages/SystemUI/res/drawable/ic_keyboard_backlight.xml +++ b/packages/SystemUI/res/drawable/ic_keyboard_backlight.xml @@ -2,11 +2,11 @@ android:viewportWidth="22" android:width="20.166666dp" xmlns:android="http://schemas.android.com/apk/res/android"> <group> <clip-path android:pathData="M0,0.5h22v11h-22z"/> - <path android:fillColor="#231F20" android:pathData="M6.397,9.908H0V11.5H6.397V9.908Z"/> - <path android:fillColor="#231F20" android:pathData="M14.199,9.908H7.801V11.5H14.199V9.908Z"/> - <path android:fillColor="#231F20" android:pathData="M11.858,0.5H10.142V6.434H11.858V0.5Z"/> - <path android:fillColor="#231F20" android:pathData="M8.348,7.129L3.885,2.975L3.823,2.932L2.668,4.003L2.621,4.046L7.084,8.2L7.146,8.243L8.301,7.172L8.348,7.129Z"/> - <path android:fillColor="#231F20" android:pathData="M18.224,2.975L18.177,2.932L13.653,7.129L14.807,8.2L14.854,8.243L19.379,4.046L18.224,2.975Z"/> - <path android:fillColor="#231F20" android:pathData="M22,9.908H15.603V11.5H22V9.908Z"/> + <path android:fillColor="@android:color/white" android:pathData="M6.397,9.908H0V11.5H6.397V9.908Z"/> + <path android:fillColor="@android:color/white" android:pathData="M14.199,9.908H7.801V11.5H14.199V9.908Z"/> + <path android:fillColor="@android:color/white" android:pathData="M11.858,0.5H10.142V6.434H11.858V0.5Z"/> + <path android:fillColor="@android:color/white" android:pathData="M8.348,7.129L3.885,2.975L3.823,2.932L2.668,4.003L2.621,4.046L7.084,8.2L7.146,8.243L8.301,7.172L8.348,7.129Z"/> + <path android:fillColor="@android:color/white" android:pathData="M18.224,2.975L18.177,2.932L13.653,7.129L14.807,8.2L14.854,8.243L19.379,4.046L18.224,2.975Z"/> + <path android:fillColor="@android:color/white" android:pathData="M22,9.908H15.603V11.5H22V9.908Z"/> </group> </vector> diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 441f963a855a..386c9d66a0c1 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -14,12 +14,17 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> + +<!-- +keep split_shade_status_bar height constant to avoid requestLayout calls on each +frame when animating QS <-> QQS transition +--> <com.android.systemui.util.NoRemeasureMotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/split_shade_status_bar" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/qs_header_height" android:minHeight="@dimen/large_screen_shade_header_min_height" android:clickable="false" android:focusable="true" @@ -30,6 +35,14 @@ app:layoutDescription="@xml/combined_qs_header_scene"> <androidx.constraintlayout.widget.Guideline + android:id="@+id/qqs_header_bottom_guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="@dimen/large_screen_shade_header_min_height" + /> + + <androidx.constraintlayout.widget.Guideline android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/begin_guide" diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml index 488be3a4479e..4e5785d284ee 100644 --- a/packages/SystemUI/res/layout/smart_action_button.xml +++ b/packages/SystemUI/res/layout/smart_action_button.xml @@ -29,8 +29,8 @@ android:textSize="@dimen/smart_reply_button_font_size" android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra" android:textColor="@color/smart_reply_button_text" - android:paddingLeft="@dimen/smart_reply_button_action_padding_left" - android:paddingRight="@dimen/smart_reply_button_padding_horizontal" + android:paddingStart="@dimen/smart_reply_button_action_padding_left" + android:paddingEnd="@dimen/smart_reply_button_padding_horizontal" android:drawablePadding="@dimen/smart_action_button_icon_padding" android:textStyle="normal" android:ellipsize="none"/> diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml index ddf16e0afed7..b24362febbdd 100644 --- a/packages/SystemUI/res/layout/smart_reply_button.xml +++ b/packages/SystemUI/res/layout/smart_reply_button.xml @@ -31,7 +31,7 @@ android:textSize="@dimen/smart_reply_button_font_size" android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra" android:textColor="@color/smart_reply_button_text" - android:paddingLeft="@dimen/smart_reply_button_padding_horizontal" - android:paddingRight="@dimen/smart_reply_button_padding_horizontal" + android:paddingStart="@dimen/smart_reply_button_padding_horizontal" + android:paddingEnd="@dimen/smart_reply_button_padding_horizontal" android:textStyle="normal" android:ellipsize="none"/> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index cb8c2a73c15d..db7eb7a049e7 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -207,11 +207,6 @@ <color name="controls_task_view_bg">#CC191C1D</color> <color name="control_popup_dim">#8A000000</color> - <!-- Keyboard backlight indicator--> - <color name="backlight_indicator_step_filled">#F6E388</color> - <color name="backlight_indicator_step_empty">#494740</color> - <color name="backlight_indicator_background">#32302A</color> - <!-- Docked misalignment message --> <color name="misalignment_text_color">#F28B82</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 160218929773..1252695d6bbb 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -889,4 +889,8 @@ --> <dimen name="shade_swipe_collapse_threshold">0.5</dimen> <!-- [END] MULTI SHADE --> + + <!-- Time (in ms) to delay the bouncer views from showing when passive auth may be used for + device entry. --> + <integer name="primary_bouncer_passive_auth_delay">250</integer> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0aa880fe6d88..aff0e8052739 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -615,6 +615,7 @@ <dimen name="qs_header_carrier_separator_width">6dp</dimen> <dimen name="qs_carrier_margin_width">4dp</dimen> <dimen name="qs_footer_icon_size">20dp</dimen> + <dimen name="qs_header_height">120dp</dimen> <dimen name="qs_header_row_min_height">48dp</dimen> <dimen name="qs_header_non_clickable_element_height">24dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 499dfa4b3c9f..eaeaabe8779a 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -198,6 +198,9 @@ <item type="id" name="pm_lite"/> <item type="id" name="settings_button_container"/> + <!--Keyboard Backlight Dialog --> + <item type="id" name="keyboard_backlight_dialog_container"/> + <item type="id" name="log_access_dialog_allow_button" /> <item type="id" name="log_access_dialog_deny_button" /> </resources> diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml index 00a0444a1c9d..1950965fc298 100644 --- a/packages/SystemUI/res/xml/qqs_header.xml +++ b/packages/SystemUI/res/xml/qqs_header.xml @@ -28,7 +28,7 @@ android:layout_height="@dimen/large_screen_shade_header_min_height" app:layout_constraintStart_toStartOf="@id/begin_guide" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" app:layout_constraintEnd_toStartOf="@id/date" app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_chainStyle="packed" @@ -62,7 +62,7 @@ app:layout_constraintStart_toEndOf="@id/date" app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" app:layout_constraintHorizontal_bias="1" app:layout_constraintHorizontal_chainStyle="packed" /> @@ -77,7 +77,7 @@ app:layout_constraintStart_toEndOf="@id/statusIcons" app:layout_constraintEnd_toEndOf="@id/end_guide" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" app:layout_constraintHorizontal_bias="1" app:layout_constraintHorizontal_chainStyle="packed" /> @@ -105,7 +105,7 @@ app:layout_constraintStart_toEndOf="@id/date" app:layout_constraintEnd_toEndOf="@id/end_guide" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" app:layout_constraintHorizontal_bias="1" /> </Constraint> diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt index 62f4f2282e66..a82f0e3e2a25 100644 --- a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt +++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt @@ -26,8 +26,8 @@ import android.content.res.TypedArray import android.graphics.Color import android.util.AttributeSet import android.view.View +import com.android.app.animation.Interpolators import com.android.settingslib.Utils -import com.android.systemui.animation.Interpolators import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.TITLE /** Displays security messages for the keyguard bouncer. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 4629e8b19ae5..644a9bcdd588 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -15,12 +15,13 @@ import android.widget.RelativeLayout; import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.keyguard.dagger.KeyguardStatusViewScope; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.ClockController; +import com.android.systemui.shared.clocks.DefaultClockController; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -46,6 +47,9 @@ public class KeyguardClockSwitch extends RelativeLayout { public static final int LARGE = 0; public static final int SMALL = 1; + // compensate for translation of parents subject to device screen + // In this case, the translation comes from KeyguardStatusView + public int screenOffsetYPadding = 0; /** Returns a region for the large clock to position itself, based on the given parent. */ public static Rect getLargeClockRegion(ViewGroup parent) { @@ -161,8 +165,18 @@ public class KeyguardClockSwitch extends RelativeLayout { } if (mLargeClockFrame.isLaidOut()) { - mClock.getLargeClock().getEvents().onTargetRegionChanged( - getLargeClockRegion(mLargeClockFrame)); + Rect targetRegion = getLargeClockRegion(mLargeClockFrame); + if (mClock instanceof DefaultClockController) { + mClock.getLargeClock().getEvents().onTargetRegionChanged( + targetRegion); + } else { + mClock.getLargeClock().getEvents().onTargetRegionChanged( + new Rect( + targetRegion.left, + targetRegion.top - screenOffsetYPadding, + targetRegion.right, + targetRegion.bottom - screenOffsetYPadding)); + } } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 50dac32d67d7..d8bf570954df 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -169,6 +169,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** + * Used for status view to pass the screen offset from parent view + */ + public void setLockscreenClockY(int clockY) { + if (mView.screenOffsetYPadding != clockY) { + mView.screenOffsetYPadding = clockY; + mView.updateClockTargetRegions(); + } + } + + /** * Attach the controller to the view it relates to. */ @Override @@ -394,13 +404,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X, x, props, animate); } - - } - - void updateKeyguardStatusViewOffset() { - // updateClockTargetRegions will call onTargetRegionChanged - // which will require the correct translationY property of keyguardStatusView after updating - mView.updateClockTargetRegions(); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index 03947542d21e..58807e4bf3bd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN; @@ -32,9 +33,9 @@ import android.view.animation.Interpolator; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; +import com.android.app.animation.Interpolators; import com.android.settingslib.animation.DisappearAnimationUtils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt; /** @@ -184,6 +185,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { } mAppearAnimator.setDuration(ANIMATION_DURATION); mAppearAnimator.addUpdateListener(animation -> animate(animation.getAnimatedFraction())); + mAppearAnimator.addListener(getAnimationListener(CUJ_LOCKSCREEN_PIN_APPEAR)); mAppearAnimator.start(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 33bea027cd20..1d7c35d0c90d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -45,11 +45,11 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.TextViewInputDisabler; import com.android.systemui.DejankUtils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; /** * Displays an alphanumeric (latin-1) key entry for the user to enter * an unlock password diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 0a91150e6c39..b4ddc9a975c2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -34,9 +34,9 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.internal.widget.LockscreenCredential; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import java.util.ArrayList; import java.util.List; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index b88d85c843e4..5cc0547b5bde 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -32,7 +32,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.START; import static androidx.constraintlayout.widget.ConstraintSet.TOP; import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT; -import static com.android.systemui.animation.InterpolatorsAndroidX.DECELERATE_QUINT; +import static com.android.app.animation.InterpolatorsAndroidX.DECELERATE_QUINT; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; import static java.lang.Integer.max; @@ -87,6 +87,7 @@ import androidx.constraintlayout.widget.ConstraintSet; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; +import com.android.app.animation.Interpolators; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -97,7 +98,6 @@ import com.android.settingslib.Utils; import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt index c9128e5881cc..96ac8ad56651 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt @@ -26,9 +26,9 @@ import android.util.MathUtils import android.view.View import android.view.ViewGroup import android.view.animation.AnimationUtils +import com.android.app.animation.Interpolators import com.android.internal.R.interpolator.fast_out_extra_slow_in import com.android.systemui.R -import com.android.systemui.animation.Interpolators /** Animates constraint layout changes for the security view. */ class KeyguardSecurityViewTransition : Transition() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index 65a71664e245..b4f124aa598a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -44,11 +44,11 @@ import androidx.slice.core.SliceQuery; import androidx.slice.widget.RowContent; import androidx.slice.widget.SliceContent; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 0826f8a8b985..794eeda86b0f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -40,11 +40,11 @@ import androidx.annotation.VisibleForTesting; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; +import com.android.app.animation.Interpolators; import com.android.internal.jank.InteractionJankMonitor; import com.android.keyguard.KeyguardClockSwitch.ClockSize; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ClockController; @@ -215,6 +215,15 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** + * Pass top margin from ClockPositionAlgorithm in NotificationPanelViewController + * Use for clock view in LS to compensate for top margin to align to the screen + * Regardless of translation from AOD and unlock gestures + */ + public void setLockscreenClockY(int clockY) { + mKeyguardClockSwitchController.setLockscreenClockY(clockY); + } + + /** * Set whether the view accessibility importance mode. */ public void setStatusAccessibilityImportance(int mode) { @@ -230,7 +239,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV * Update position of the view with an optional animation */ public void updatePosition(int x, int y, float scale, boolean animate) { - float oldY = mView.getY(); setProperty(AnimatableProperty.Y, y, animate); ClockController clock = mKeyguardClockSwitchController.getClock(); @@ -246,10 +254,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV setProperty(AnimatableProperty.SCALE_X, 1f, animate); setProperty(AnimatableProperty.SCALE_Y, 1f, animate); } - - if (oldY != y) { - mKeyguardClockSwitchController.updateKeyguardStatusViewOffset(); - } } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 7d7b276fbe44..c1f70fbe8875 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2884,7 +2884,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } + /** + * If the current state of the device allows for triggering active unlock. This does not + * include active unlock availability. + */ + public boolean canTriggerActiveUnlockBasedOnDeviceState() { + return shouldTriggerActiveUnlock(/* shouldLog */ false); + } + private boolean shouldTriggerActiveUnlock() { + return shouldTriggerActiveUnlock(/* shouldLog */ true); + } + + private boolean shouldTriggerActiveUnlock(boolean shouldLog) { // Triggers: final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant(); final boolean awakeKeyguard = mPrimaryBouncerFullyShown || mAlternateBouncerShowing @@ -2914,19 +2926,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && !mKeyguardGoingAway && !mSecureCameraLaunched; - // Aggregate relevant fields for debug logging. - logListenerModelData( - new KeyguardActiveUnlockModel( - System.currentTimeMillis(), - user, - shouldTriggerActiveUnlock, - awakeKeyguard, - mAuthInterruptActive, - fpLockedOut, - primaryAuthRequired, - mSwitchingUser, - triggerActiveUnlockForAssistant, - userCanDismissLockScreen)); + if (shouldLog) { + // Aggregate relevant fields for debug logging. + logListenerModelData( + new KeyguardActiveUnlockModel( + System.currentTimeMillis(), + user, + shouldTriggerActiveUnlock, + awakeKeyguard, + mAuthInterruptActive, + fpLockedOut, + primaryAuthRequired, + mSwitchingUser, + triggerActiveUnlockForAssistant, + userCanDismissLockScreen)); + } return shouldTriggerActiveUnlock; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index d8568ba56ebc..61af7228fe0d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -21,7 +21,7 @@ import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import android.util.Property; import android.view.View; -import com.android.systemui.animation.Interpolators; +import com.android.app.animation.Interpolators; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogLevel; import com.android.systemui.statusbar.StatusBarState; diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java index c6c7113d7f9a..7d76f12880df 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java @@ -37,7 +37,7 @@ import android.widget.TextView; import androidx.annotation.StyleRes; -import com.android.systemui.animation.Interpolators; +import com.android.app.animation.Interpolators; /** * Provides background color and radius animations for key pad buttons. diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java index 4aeab970e2f7..4557b3418e19 100644 --- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java +++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java @@ -37,9 +37,9 @@ import android.widget.LinearLayout; import androidx.core.graphics.drawable.DrawableCompat; +import com.android.app.animation.Interpolators; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; /** * This class contains implementation for methods that will be used when user has set a diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt index de82ca014722..c1871e09a791 100644 --- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt +++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt @@ -36,7 +36,7 @@ import android.view.Surface import android.view.View import androidx.annotation.VisibleForTesting import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.util.asIndenting import java.io.PrintWriter diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt index 48805be50aa9..76086dffd263 100644 --- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt @@ -34,7 +34,7 @@ import androidx.core.graphics.ColorUtils import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.settingslib.Utils -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.biometrics.AuthController import com.android.systemui.log.ScreenDecorationsLogger import com.android.systemui.plugins.statusbar.StatusBarStateController diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index aa94ad98f3e6..99d46629ad7a 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -43,8 +43,8 @@ import android.view.accessibility.AccessibilityEvent; import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.internal.dynamicanimation.animation.SpringForce; -import com.android.systemui.animation.Interpolators; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.FalsingManager; diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java index d6f0b59accb1..d49197557dc4 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java @@ -32,8 +32,8 @@ import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; +import com.android.app.animation.Interpolators; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; /** * Visually discloses that contextual data was provided to an assistant. diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index 0002ae95f476..2aac056b0bde 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -45,9 +45,9 @@ import android.widget.TextView; import androidx.annotation.StyleRes; import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.systemui.DualToneHandler; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.policy.BatteryController; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b386bc9d050b..ce85124fa7a7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -59,11 +59,11 @@ import android.widget.ScrollView; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.biometrics.AuthController.ScaleFactorProvider; import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor; import com.android.systemui.biometrics.ui.CredentialView; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 55f6d072563b..6f0f6331ef50 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -32,7 +32,7 @@ import com.android.settingslib.Utils import com.android.settingslib.udfps.UdfpsOverlayParams import com.android.systemui.CoreStartable import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt index b0071340cf1a..5ede16d221b7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt @@ -28,7 +28,7 @@ import android.util.AttributeSet import android.view.View import android.view.animation.PathInterpolator import com.android.internal.graphics.ColorUtils -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.surfaceeffects.ripple.RippleShader private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt index ef7dcb7aac93..1dbafc6519f0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt @@ -19,7 +19,7 @@ import android.animation.ValueAnimator import android.graphics.PointF import android.graphics.RectF import com.android.systemui.Dumpable -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionListener diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 7a237591a212..5ee38c3c9d8f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -553,6 +553,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { + mOverlay.getRequestId()); return false; } + if (mLockscreenShadeTransitionController.getQSDragProgress() != 0f + || mPrimaryBouncerInteractor.isInTransit()) { + return false; + } final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId, mOverlayParams); @@ -626,9 +630,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { shouldPilfer = true; } - // Execute the pilfer, never pilfer if a vertical swipe is in progress - if (shouldPilfer && mLockscreenShadeTransitionController.getQSDragProgress() == 0f - && !mPrimaryBouncerInteractor.isInTransit()) { + // Execute the pilfer + if (shouldPilfer) { mInputManager.pilferPointers( mOverlay.getOverlayView().getViewRootImpl().getInputToken()); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index ba8e60a08a1d..52db4ab8b593 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -40,9 +40,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.asynclayoutinflater.view.AsyncLayoutInflater; +import com.android.app.animation.Interpolators; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.airbnb.lottie.LottieAnimationView; import com.airbnb.lottie.LottieProperty; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt index 935de0255c01..9f5669f91280 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt @@ -23,11 +23,11 @@ import android.view.MotionEvent import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator -import com.android.systemui.animation.Interpolators import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt index e2d36dc6abe1..9292bd7ecd60 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt @@ -6,8 +6,8 @@ import android.widget.ImageView import android.widget.TextView import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators import com.android.systemui.R -import com.android.systemui.animation.Interpolators import com.android.systemui.biometrics.AuthDialog import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.ui.CredentialPasswordView diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java index 11ef749573b7..7bf8f4dac1fb 100644 --- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java +++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java @@ -30,9 +30,9 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; +import com.android.app.animation.Interpolators; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.surfaceeffects.ripple.RippleShader; import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape; diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt index 8d0edf829416..b447d66c08dd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt @@ -32,7 +32,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.controls.ui.ControlsUiController object ControlsAnimations { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 6a9aaf865251..e6361f46c8ad 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -50,7 +50,7 @@ import androidx.annotation.ColorInt import androidx.annotation.VisibleForTesting import com.android.internal.graphics.ColorUtils import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.controller.ControlsController import com.android.systemui.util.concurrency.DelayableExecutor diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index fa36eee7c644..1461135d3d3b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -38,7 +38,7 @@ import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL import java.util.IllegalFormatException diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 89c45d7f5e12..17cf8084df1f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -69,6 +69,7 @@ import com.android.systemui.qs.FgsManagerControllerImpl; import com.android.systemui.qs.QSFragmentStartableModule; import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; +import com.android.systemui.retail.dagger.RetailModeModule; import com.android.systemui.screenrecord.ScreenRecordModule; import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; @@ -179,6 +180,7 @@ import javax.inject.Named; PrivacyModule.class, QRCodeScannerModule.class, QSFragmentStartableModule.class, + RetailModeModule.class, ScreenshotModule.class, SensorModule.class, SecurityRepositoryModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index df46e07e0911..c5e7e0d99286 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -25,8 +25,8 @@ import androidx.core.animation.doOnCancel import androidx.core.animation.doOnEnd import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators import com.android.systemui.R -import com.android.systemui.animation.Interpolators import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutParams import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 15a32d21213f..c22019e96d74 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -33,9 +33,9 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; +import com.android.app.animation.Interpolators; import com.android.dream.lowlight.LowLightTransitionCoordinator; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.complication.ComplicationHostViewController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.dagger.DreamOverlayComponent; diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 1fafa3d97c48..d1d53cc9dd58 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -702,6 +702,5 @@ object Flags { // TODO(b/278761837): Tracking Bug @JvmField - val USE_NEW_ACTIVITY_STARTER = unreleasedFlag(2801, name = "use_new_activity_starter", - teamfood = true) + val USE_NEW_ACTIVITY_STARTER = releasedFlag(2801, name = "use_new_activity_starter") } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index f64ed6078ddb..280710755ff6 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -97,6 +97,7 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; +import com.android.app.animation.Interpolators; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; @@ -116,7 +117,6 @@ import com.android.systemui.MultiListLayout.MultiListAdapter; import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.animation.Expandable; -import com.android.systemui.animation.Interpolators; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Background; diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt index 2ef5e19bf382..6bc763ce421e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyboard.backlight.ui.view +import android.annotation.AttrRes import android.annotation.ColorInt import android.app.Dialog import android.content.Context @@ -31,6 +32,7 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.LinearLayout.LayoutParams import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT +import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.util.children @@ -38,7 +40,7 @@ class KeyboardBacklightDialog( context: Context, initialCurrentLevel: Int, initialMaxLevel: Int, -) : Dialog(context) { +) : Dialog(context, R.style.Theme_SystemUI_Dialog) { private data class RootProperties( val cornerRadius: Float, @@ -69,9 +71,14 @@ class KeyboardBacklightDialog( private lateinit var rootProperties: RootProperties private lateinit var iconProperties: BacklightIconProperties private lateinit var stepProperties: StepViewProperties - @ColorInt var filledRectangleColor: Int = 0 - @ColorInt var emptyRectangleColor: Int = 0 - @ColorInt var backgroundColor: Int = 0 + @ColorInt + var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary) + @ColorInt + var emptyRectangleColor = + getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant) + @ColorInt + var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright) + @ColorInt var iconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary) init { currentLevel = initialCurrentLevel @@ -90,9 +97,6 @@ class KeyboardBacklightDialog( private fun updateResources() { context.resources.apply { - filledRectangleColor = getColor(R.color.backlight_indicator_step_filled, context.theme) - emptyRectangleColor = getColor(R.color.backlight_indicator_step_empty, context.theme) - backgroundColor = getColor(R.color.backlight_indicator_background, context.theme) rootProperties = RootProperties( cornerRadius = @@ -126,6 +130,11 @@ class KeyboardBacklightDialog( } } + @ColorInt + fun getColorFromStyle(@AttrRes colorId: Int): Int { + return Utils.getColorAttrDefaultColor(context, colorId) + } + fun updateState(current: Int, max: Int, forceRefresh: Boolean = false) { if (maxLevel != max || forceRefresh) { maxLevel = max @@ -159,6 +168,7 @@ class KeyboardBacklightDialog( private fun buildRootView(): LinearLayout { val linearLayout = LinearLayout(context).apply { + id = R.id.keyboard_backlight_dialog_container orientation = LinearLayout.HORIZONTAL layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT) setPadding( @@ -214,6 +224,7 @@ class KeyboardBacklightDialog( private fun createBacklightIconView(): ImageView { return ImageView(context).apply { setImageResource(R.drawable.ic_keyboard_backlight) + setColorFilter(iconColor) layoutParams = FrameLayout.LayoutParams(iconProperties.width, iconProperties.height).apply { gravity = Gravity.CENTER diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 2925d8dbd03a..9844ca02482b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -36,7 +36,7 @@ import androidx.core.math.MathUtils import com.android.internal.R import com.android.keyguard.KeyguardClockSwitchController import com.android.keyguard.KeyguardViewController -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 1a126d72f9e9..2854de011ec6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -98,6 +98,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.internal.policy.IKeyguardDismissCallback; @@ -121,7 +122,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; -import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt index ef8b40191149..ee0eb2d90aa7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt @@ -46,8 +46,6 @@ interface KeyguardFaceAuthModule { impl: SystemUIKeyguardFaceAuthInteractor ): KeyguardFaceAuthInteractor - @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository - companion object { @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index f27f89995dbd..e7b9af62dacf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -45,4 +45,6 @@ interface KeyguardRepositoryModule { @Binds fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository + + @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt index d90f328719bb..74c552085be6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt @@ -28,7 +28,9 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -40,6 +42,9 @@ import kotlinx.coroutines.flow.shareIn interface TrustRepository { /** Flow representing whether the current user is trusted. */ val isCurrentUserTrusted: Flow<Boolean> + + /** Flow representing whether active unlock is available for the current user. */ + val isCurrentUserActiveUnlockAvailable: StateFlow<Boolean> } @SysUISingleton @@ -89,11 +94,13 @@ constructor( } .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1) - override val isCurrentUserTrusted: Flow<Boolean> - get() = - combine(trust, userRepository.selectedUserInfo, ::Pair) - .map { latestTrustModelForUser[it.second.id]?.isTrusted ?: false } - .distinctUntilChanged() - .onEach { logger.isCurrentUserTrusted(it) } - .onStart { emit(false) } + override val isCurrentUserTrusted: Flow<Boolean> = + combine(trust, userRepository.selectedUserInfo, ::Pair) + .map { latestTrustModelForUser[it.second.id]?.isTrusted ?: false } + .distinctUntilChanged() + .onEach { logger.isCurrentUserTrusted(it) } + .onStart { emit(false) } + + // TODO: Implement based on TrustManager callback b/267322286 + override val isCurrentUserActiveUnlockAvailable: StateFlow<Boolean> = MutableStateFlow(true) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index e6568f20bc20..cde67f979132 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index c2d139c21074..7e9cbc1a9772 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 86f65dde4031..aca40195dbcf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 3beac0b1322e..fc7bfb4e45f4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index b5bcd45f03dd..39c630b1fa6f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 87f3164b33d2..0505d37262b0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index c0d5abc04e5d..47846d1f4118 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index a681c43be19f..bc55bd4b8c92 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -17,10 +17,10 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator +import com.android.app.animation.Interpolators import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt index 970d00403f28..110bcd715be2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt @@ -32,14 +32,16 @@ import com.android.systemui.R import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.plugins.ActivityStarter import com.android.systemui.shared.system.SysUiStatsLog -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -56,28 +58,21 @@ import javax.inject.Inject class PrimaryBouncerInteractor @Inject constructor( - private val repository: KeyguardBouncerRepository, - private val primaryBouncerView: BouncerView, - @Main private val mainHandler: Handler, - private val keyguardStateController: KeyguardStateController, - private val keyguardSecurityModel: KeyguardSecurityModel, - private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor, - private val falsingCollector: FalsingCollector, - private val dismissCallbackRegistry: DismissCallbackRegistry, - private val context: Context, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - keyguardBypassController: KeyguardBypassController, + private val repository: KeyguardBouncerRepository, + private val primaryBouncerView: BouncerView, + @Main private val mainHandler: Handler, + private val keyguardStateController: KeyguardStateController, + private val keyguardSecurityModel: KeyguardSecurityModel, + private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor, + private val falsingCollector: FalsingCollector, + private val dismissCallbackRegistry: DismissCallbackRegistry, + private val context: Context, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val trustRepository: TrustRepository, + private val featureFlags: FeatureFlags, ) { - /** Whether we want to wait for face auth. */ - private val primaryBouncerFaceDelay = - keyguardStateController.isFaceAuthEnabled && - !keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( - KeyguardUpdateMonitor.getCurrentUser() - ) && - !needsFullscreenBouncer() && - keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) && - !keyguardBypassController.bypassEnabled - + private val passiveAuthBouncerDelay = context.resources.getInteger( + R.integer.primary_bouncer_passive_auth_delay).toLong() /** Runnable to show the primary bouncer. */ val showRunnable = Runnable { repository.setPrimaryShow(true) @@ -160,8 +155,9 @@ constructor( } repository.setPrimaryShowingSoon(true) - if (primaryBouncerFaceDelay) { - mainHandler.postDelayed(showRunnable, 1200L) + if (usePrimaryBouncerPassiveAuthDelay()) { + Log.d(TAG, "delay bouncer, passive auth may succeed") + mainHandler.postDelayed(showRunnable, passiveAuthBouncerDelay) } else { DejankUtils.postAfterTraversal(showRunnable) } @@ -377,6 +373,17 @@ constructor( return repository.primaryBouncerShow.value } + /** Whether we want to wait to show the bouncer in case passive auth succeeds. */ + private fun usePrimaryBouncerPassiveAuthDelay(): Boolean { + val canRunFaceAuth = keyguardStateController.isFaceAuthEnabled && + keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) + val canRunActiveUnlock = trustRepository.isCurrentUserActiveUnlockAvailable.value && + keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState() + return featureFlags.isEnabled(Flags.DELAY_BOUNCER) && + !needsFullscreenBouncer() && + (canRunFaceAuth || canRunActiveUnlock) + } + companion object { private const val TAG = "PrimaryBouncerInteractor" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt index 38b9d508f81c..9d7477c13be6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui import android.view.animation.Interpolator -import com.android.systemui.animation.Interpolators.LINEAR +import com.android.app.animation.Interpolators.LINEAR import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index d96609c24dbd..c8d37a165a0e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -32,11 +32,11 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.Expandable -import com.android.systemui.animation.Interpolators import com.android.systemui.animation.view.LaunchableLinearLayout import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.binder.IconViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index 951f0bd76a64..ad11360160c1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.ui.preview +import android.annotation.ColorInt import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -85,6 +86,7 @@ constructor( private var clockView: View? = null private var smartSpaceView: View? = null + private var colorOverride: Int? = null private val disposables = mutableSetOf<DisposableHandle>() private var isDestroyed = false @@ -171,6 +173,14 @@ constructor( } } + /** Sets the clock's color to the overridden seed color. */ + fun onColorOverridden(@ColorInt color: Int?) { + runBlocking(mainDispatcher) { + colorOverride = color + clockController.clock?.run { events.onSeedColorChanged(color) } + } + } + /** * This sets up and shows a non-interactive smart space * @@ -288,8 +298,10 @@ constructor( val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - clockController.clock?.smallClock?.events?.onTimeTick() - clockController.clock?.largeClock?.events?.onTimeTick() + clockController.clock?.run { + smallClock.events.onTimeTick() + largeClock.events.onTimeTick() + } } } broadcastDispatcher.registerReceiver( @@ -305,18 +317,18 @@ constructor( } private fun onClockChanged(parentView: ViewGroup) { - clockController.clock = clockRegistry.createCurrentClock() + val clock = clockRegistry.createCurrentClock() + clockController.clock = clock + colorOverride?.let { clock.events.onSeedColorChanged(it) } if (!shouldHideClock) { - val largeClock = clockController.clock?.largeClock - - largeClock - ?.events - ?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView)) + clock.largeClock.events.onTargetRegionChanged( + KeyguardClockSwitch.getLargeClockRegion(parentView) + ) clockView?.let { parentView.removeView(it) } clockView = - largeClock?.view?.apply { + clock.largeClock.view.apply { if (shouldHighlightSelectedAffordance) { alpha = DIM_ALPHA } @@ -329,7 +341,7 @@ constructor( // Hide smart space if the clock has weather display; otherwise show it val hasCustomWeatherDataDisplay = - clockController.clock?.largeClock?.config?.hasCustomWeatherDataDisplay == true + clock.largeClock.config.hasCustomWeatherDataDisplay == true hideSmartspace(hasCustomWeatherDataDisplay) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index 3869b23900d1..79712f9c6be5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -115,16 +115,21 @@ constructor( when (message.what) { KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> { - message.data - .getString( - KeyguardPreviewConstants.KEY_SLOT_ID, - ) - ?.let { slotId -> renderer.onSlotSelected(slotId = slotId) } + message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId -> + renderer.onSlotSelected(slotId = slotId) + } } KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> { - message.data - .getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE) - .let { hide -> renderer.hideSmartspace(hide) } + renderer.hideSmartspace( + message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE) + ) + } + KeyguardPreviewConstants.MESSAGE_ID_COLOR_OVERRIDE -> { + renderer.onColorOverridden( + message.data + .getString(KeyguardPreviewConstants.KEY_COLOR_OVERRIDE) + ?.toIntOrNull() + ) } else -> requestDestruction(this) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt index 8d6545a49a8a..2c9a9b3271e6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt @@ -16,8 +16,8 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE -import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE +import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt index f16827d4a54a..c1357863f3a5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt index bc9dc4f69102..c6187dde035b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt index a60665a81f0e..d3ea89ce1935 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt index ddce516a0fb2..6845c55b8385 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE +import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt index df93d235245c..68810f909016 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt index aec7b5f7aa7a..c41f82b4ddd1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt @@ -111,7 +111,7 @@ class MediaProjectionAppSelectorActivity( if (featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) { component.emptyStateProvider } else { - super.createBlockerEmptyStateProvider() + object : EmptyStateProvider {} } override fun createListController(userHandle: UserHandle): ResolverListController = diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java index 28adfbba1103..c9c2ea27e5f4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java @@ -79,6 +79,9 @@ public class MediaProjectionPermissionActivity extends Activity // Indicates if user must review already-granted consent that the MediaProjection app is // attempting to re-use. private boolean mReviewGrantedConsentRequired = false; + // Indicates if the user has consented to record, but is continuing in another activity to + // select a particular task to capture. + private boolean mUserSelectingTask = false; @Inject public MediaProjectionPermissionActivity(FeatureFlags featureFlags, @@ -296,6 +299,7 @@ public class MediaProjectionPermissionActivity extends Activity // Start activity from the current foreground user to avoid creating a separate // SystemUI process without access to recent tasks because it won't have // WM Shell running inside. + mUserSelectingTask = true; startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser())); } } catch (RemoteException e) { @@ -316,7 +320,10 @@ public class MediaProjectionPermissionActivity extends Activity @Override public void finish() { // Default to cancelling recording when user needs to review consent. - finish(RECORD_CANCEL, /* projection= */ null); + // Don't send cancel if the user has moved on to the next activity. + if (!mUserSelectingTask) { + finish(RECORD_CANCEL, /* projection= */ null); + } } private void finish(@ReviewGrantedConsentResult int consentResult, @@ -328,7 +335,7 @@ public class MediaProjectionPermissionActivity extends Activity private void onDialogDismissedOrCancelled(DialogInterface dialogInterface) { if (!isFinishing()) { - finish(RECORD_CANCEL, /* projection= */ null); + finish(); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt index 37d956bd09eb..e38abc2228c4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt @@ -21,9 +21,9 @@ import android.animation.ObjectAnimator import android.text.format.DateUtils import androidx.annotation.UiThread import androidx.lifecycle.Observer +import com.android.app.animation.Interpolators import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R -import com.android.systemui.animation.Interpolators import com.android.systemui.media.controls.ui.SquigglyProgress /** diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt index 3669493f4e41..b46ebb22ff05 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt @@ -34,10 +34,10 @@ import android.util.AttributeSet import android.util.MathUtils import android.view.View import androidx.annotation.Keep +import com.android.app.animation.Interpolators import com.android.internal.graphics.ColorUtils import com.android.internal.graphics.ColorUtils.blendARGB import com.android.systemui.R -import com.android.systemui.animation.Interpolators import org.xmlpull.v1.XmlPullParser private const val BACKGROUND_ANIM_DURATION = 370L diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt index dd5c2bf497cb..937a618df68f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt @@ -35,9 +35,9 @@ import android.graphics.drawable.Drawable import android.util.AttributeSet import android.util.MathUtils.lerp import androidx.annotation.Keep +import com.android.app.animation.Interpolators import com.android.internal.graphics.ColorUtils import com.android.systemui.R -import com.android.systemui.animation.Interpolators import org.xmlpull.v1.XmlPullParser private const val RIPPLE_ANIM_DURATION = 800L diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index 40027a1d8f2c..f9d3094d6f00 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -72,6 +72,7 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.constraintlayout.widget.ConstraintSet; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.jank.InteractionJankMonitor; @@ -82,7 +83,6 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.GhostedViewLaunchAnimatorController; -import com.android.systemui.animation.Interpolators; import com.android.systemui.bluetooth.BroadcastDialogController; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.qualifiers.Background; diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index 49e1665a0de9..fe8ebafdf9b4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -33,9 +33,9 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay import androidx.annotation.VisibleForTesting +import com.android.app.animation.Interpolators import com.android.keyguard.KeyguardViewController import com.android.systemui.R -import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dreams.DreamOverlayStateController diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt index e9b2cf2b18d1..583c626d2156 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt @@ -31,8 +31,8 @@ import android.util.MathUtils.lerp import android.util.MathUtils.lerpInv import android.util.MathUtils.lerpInvSat import androidx.annotation.VisibleForTesting +import com.android.app.animation.Interpolators import com.android.internal.graphics.ColorUtils -import com.android.systemui.animation.Interpolators import kotlin.math.abs import kotlin.math.cos diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 78082c3eb3c6..77ff0362851a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -36,7 +36,7 @@ import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE import com.android.internal.widget.CachingIconView import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.ui.binder.TintedIconViewBinder import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt index 219629b44c5d..1d8fe729494d 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt @@ -19,7 +19,6 @@ package com.android.systemui.mediaprojection.appselector import android.content.ComponentName import android.os.UserHandle import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider import javax.inject.Inject @@ -59,13 +58,7 @@ constructor( * Removes all recent tasks that are different from the profile of the host app to avoid any * cross-profile sharing */ - private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = - if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) { - // TODO(b/263950746): filter tasks based on the enterprise policies - this - } else { - filter { UserHandle.of(it.userId) == hostUserHandle } - } + private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = this private fun List<RecentTask>.filterAppSelector(): List<RecentTask> = filter { // Only take tasks that is not the app selector diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 94f01b87f936..146b5f57630e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -58,11 +58,11 @@ import android.widget.FrameLayout; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.Utils; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.buttons.ButtonDispatcher; import com.android.systemui.navigationbar.buttons.ContextualButton; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java index 02180160fc11..10084bd4ccad 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java @@ -16,7 +16,7 @@ package com.android.systemui.navigationbar.buttons; -import static com.android.systemui.animation.Interpolators.LINEAR; +import static com.android.app.animation.Interpolators.LINEAR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -24,9 +24,6 @@ import android.animation.ValueAnimator; import android.view.View; import android.view.View.AccessibilityDelegate; -import com.android.systemui.Dependency; -import com.android.systemui.assist.AssistManager; - import java.util.ArrayList; /** diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java index 590efbb66454..ff22398cc3be 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java @@ -48,10 +48,10 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; +import com.android.app.animation.Interpolators; import com.android.internal.util.LatencyTracker; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.settings.DisplayTracker; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index a7aac5a4824d..463c79c6696a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -26,7 +26,7 @@ import android.view.View.OnLayoutChangeListener; import androidx.annotation.Nullable; -import com.android.systemui.animation.Interpolators; +import com.android.app.animation.Interpolators; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.QSTile; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index 0d29a1abca9f..85f557b10762 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -27,6 +27,7 @@ import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.dagger.QSScope; +import com.android.systemui.retail.domain.interactor.RetailModeInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.ViewController; @@ -45,18 +46,22 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme private final View mEditButton; private final FalsingManager mFalsingManager; private final ActivityStarter mActivityStarter; + private final RetailModeInteractor mRetailModeInteractor; @Inject QSFooterViewController(QSFooterView view, UserTracker userTracker, FalsingManager falsingManager, ActivityStarter activityStarter, - QSPanelController qsPanelController) { + QSPanelController qsPanelController, + RetailModeInteractor retailModeInteractor + ) { super(view); mUserTracker = userTracker; mQsPanelController = qsPanelController; mFalsingManager = falsingManager; mActivityStarter = activityStarter; + mRetailModeInteractor = retailModeInteractor; mBuildText = mView.findViewById(R.id.build); mPageIndicator = mView.findViewById(R.id.footer_page_indicator); @@ -96,6 +101,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme @Override public void setVisibility(int visibility) { mView.setVisibility(visibility); + mEditButton + .setVisibility(mRetailModeInteractor.isInRetailMode() ? View.GONE : View.VISIBLE); mEditButton.setClickable(visibility == View.VISIBLE); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index fd3f70148a69..b8c2fad8c10d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -44,10 +44,10 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; +import com.android.app.animation.Interpolators; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.compose.ComposeFacade; import com.android.systemui.dump.DumpManager; diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt index 595b29a9dcb8..3b2362f2b326 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt @@ -20,6 +20,7 @@ import android.annotation.UserIdInt import android.content.res.Resources import android.database.ContentObserver import android.provider.Settings +import com.android.systemui.R import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -27,12 +28,16 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.QSHost import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger +import com.android.systemui.retail.data.repository.RetailModeRepository import com.android.systemui.util.settings.SecureSettings import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -84,6 +89,9 @@ interface TileSpecRepository { * [Settings.Secure.QS_TILES]. * * All operations against [Settings] will be performed in a background thread. + * + * If the device is in retail mode, the tiles are fixed to the value of + * [R.string.quick_settings_tiles_retail_mode]. */ @SysUISingleton class TileSpecSettingsRepository @@ -92,9 +100,31 @@ constructor( private val secureSettings: SecureSettings, @Main private val resources: Resources, private val logger: QSPipelineLogger, + private val retailModeRepository: RetailModeRepository, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : TileSpecRepository { + + private val retailModeTiles by lazy { + resources + .getString(R.string.quick_settings_tiles_retail_mode) + .split(DELIMITER) + .map(TileSpec::create) + .filter { it !is TileSpec.Invalid } + } + + @OptIn(ExperimentalCoroutinesApi::class) override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> { + return retailModeRepository.retailMode.flatMapLatest { inRetailMode -> + if (inRetailMode) { + logger.logUsingRetailTiles() + flowOf(retailModeTiles) + } else { + settingsTiles(userId) + } + } + } + + private fun settingsTiles(userId: Int): Flow<List<TileSpec>> { return conflatedCallbackFlow { val observer = object : ContentObserver(null) { @@ -157,6 +187,10 @@ constructor( } private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) { + if (retailModeRepository.inRetailMode) { + // No storing tiles when in retail mode + return + } val toStore = tiles .filter { it !is TileSpec.Invalid } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt index b564334b5c52..ff7d2068bc4e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt @@ -120,6 +120,10 @@ constructor( ) } + fun logUsingRetailTiles() { + tileListLogBuffer.log(TILE_LIST_TAG, LogLevel.DEBUG, {}, { "Using retail tiles" }) + } + /** Reasons for destroying an existing tile. */ enum class TileDestroyedReason(val readable: String) { TILE_REMOVED("Tile removed from current set"), diff --git a/packages/SystemUI/src/com/android/systemui/retail/dagger/RetailModeModule.kt b/packages/SystemUI/src/com/android/systemui/retail/dagger/RetailModeModule.kt new file mode 100644 index 000000000000..e8639498b7c0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/retail/dagger/RetailModeModule.kt @@ -0,0 +1,34 @@ +/* + * 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.retail.dagger + +import com.android.systemui.retail.data.repository.RetailModeRepository +import com.android.systemui.retail.data.repository.RetailModeSettingsRepository +import com.android.systemui.retail.domain.interactor.RetailModeInteractor +import com.android.systemui.retail.domain.interactor.RetailModeInteractorImpl +import dagger.Binds +import dagger.Module + +@Module +abstract class RetailModeModule { + + @Binds + abstract fun bindsRetailModeRepository(impl: RetailModeSettingsRepository): RetailModeRepository + + @Binds + abstract fun bindsRetailModeInteractor(impl: RetailModeInteractorImpl): RetailModeInteractor +} diff --git a/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt new file mode 100644 index 000000000000..3c0aa384d98c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt @@ -0,0 +1,81 @@ +/* + * 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.retail.data.repository + +import android.database.ContentObserver +import android.provider.Settings +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.settings.GlobalSettings +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +/** Repository to track if the device is in Retail mode */ +interface RetailModeRepository { + /** Flow of whether the device is currently in retail mode. */ + val retailMode: StateFlow<Boolean> + + /** Last value of whether the device is in retail mode. */ + val inRetailMode: Boolean + get() = retailMode.value +} + +/** + * Tracks [Settings.Global.DEVICE_DEMO_MODE]. + * + * @see UserManager.isDeviceInDemoMode + */ +@SysUISingleton +class RetailModeSettingsRepository +@Inject +constructor( + globalSettings: GlobalSettings, + @Background backgroundDispatcher: CoroutineDispatcher, + @Application scope: CoroutineScope, +) : RetailModeRepository { + override val retailMode = + conflatedCallbackFlow { + val observer = + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + trySend(Unit) + } + } + + globalSettings.registerContentObserver(RETAIL_MODE_SETTING, observer) + + awaitClose { globalSettings.unregisterContentObserver(observer) } + } + .onStart { emit(Unit) } + .map { globalSettings.getInt(RETAIL_MODE_SETTING, 0) != 0 } + .flowOn(backgroundDispatcher) + .stateIn(scope, SharingStarted.Eagerly, false) + + companion object { + private const val RETAIL_MODE_SETTING = Settings.Global.DEVICE_DEMO_MODE + } +} diff --git a/packages/SystemUI/src/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt new file mode 100644 index 000000000000..eea452c78ea5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt @@ -0,0 +1,37 @@ +/* + * 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.retail.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.retail.data.repository.RetailModeRepository +import javax.inject.Inject + +/** Interactor to determine if the device is currently in retail mode */ +interface RetailModeInteractor { + /** Whether the device is currently in retail mode */ + val isInRetailMode: Boolean +} + +@SysUISingleton +class RetailModeInteractorImpl +@Inject +constructor( + private val repository: RetailModeRepository, +) : RetailModeInteractor { + override val isInRetailMode: Boolean + get() = repository.inRetailMode +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 137a99ef39b8..926ede99f5a9 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -22,10 +22,10 @@ import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; +import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE; +import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; -import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE; -import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -88,6 +88,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.widget.FrameLayout; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; @@ -111,7 +112,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; -import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.Classifier; @@ -1513,6 +1513,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard), mKeyguardStatusViewController.isClockTopAligned()); mClockPositionAlgorithm.run(mClockPositionResult); + mKeyguardStatusViewController.setLockscreenClockY( + mClockPositionAlgorithm.getExpandedPreferredClockY()); mKeyguardBottomAreaInteractor.setClockPosition( mClockPositionResult.clockX, mClockPositionResult.clockY); boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index fb7c5c2e31fa..31b361f83758 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -18,7 +18,6 @@ package com.android.systemui.shade import android.view.View import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.WindowInsets import androidx.annotation.VisibleForTesting import androidx.constraintlayout.widget.ConstraintSet @@ -62,6 +61,7 @@ class NotificationsQSContainerController @Inject constructor( private var isQSCustomizing = false private var isQSCustomizerAnimating = false + private var shadeHeaderHeight = 0 private var largeScreenShadeHeaderHeight = 0 private var largeScreenShadeHeaderActive = false private var notificationsBottomMargin = 0 @@ -146,6 +146,8 @@ class NotificationsQSContainerController @Inject constructor( R.dimen.notification_panel_margin_bottom) largeScreenShadeHeaderHeight = resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height) + shadeHeaderHeight = + resources.getDimensionPixelSize(R.dimen.qs_header_height) panelMarginHorizontal = resources.getDimensionPixelSize( R.dimen.notification_panel_margin_horizontal) topMargin = if (largeScreenShadeHeaderActive) { @@ -245,7 +247,7 @@ class NotificationsQSContainerController @Inject constructor( if (largeScreenShadeHeaderActive) { constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight) } else { - constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT) + constraintSet.constrainHeight(R.id.split_shade_status_bar, shadeHeaderHeight) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index a1fa8fbf9075..abdd1a94d93c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -50,6 +50,7 @@ import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; @@ -59,7 +60,6 @@ import com.android.internal.policy.SystemBarUtils; import com.android.keyguard.FaceAuthApiRequestReason; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 86ae4ecf6e70..f080d3dfab1d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -34,10 +34,10 @@ import android.view.WindowInsets import android.widget.TextView import androidx.annotation.VisibleForTesting import androidx.constraintlayout.motion.widget.MotionLayout +import com.android.app.animation.Interpolators import com.android.settingslib.Utils import com.android.systemui.Dumpable import com.android.systemui.R -import com.android.systemui.animation.Interpolators import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java index 63179dac7b8c..c1ebf1236def 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java @@ -18,8 +18,8 @@ package com.android.systemui.statusbar; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java index 54b341f529b0..1a32d70a9745 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java @@ -38,8 +38,8 @@ import android.view.ViewAnimationUtils; import android.view.animation.Interpolator; import android.widget.ImageView; +import com.android.app.animation.Interpolators; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.wm.shell.animation.FlingAnimationUtils; /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 9421524c73a7..823bb355a307 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -16,7 +16,7 @@ import android.util.AttributeSet import android.util.MathUtils.lerp import android.view.View import android.view.animation.PathInterpolator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold import com.android.systemui.util.getColorWithAlpha import com.android.systemui.util.leak.RotationUtils diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 7f016f304e05..b61f2438324b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -18,7 +18,7 @@ import com.android.systemui.Dumpable import com.android.systemui.ExpandHelper import com.android.systemui.Gefingerpoken import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.biometrics.UdfpsKeyguardViewController import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.FalsingCollector diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 72ae16e607c8..fb88a96c38c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -44,9 +44,9 @@ import android.util.Log; import android.view.View; import android.widget.ImageView; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; -import com.android.systemui.animation.Interpolators; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index a3bd247668fe..0e20df6ecfba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -33,7 +33,7 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringAnimation import androidx.dynamicanimation.animation.SpringForce import com.android.systemui.Dumpable -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 49c7950fd923..9d7f3be4dfe7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -35,10 +35,10 @@ import android.view.animation.PathInterpolator; import androidx.annotation.NonNull; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 821a17279cb3..fc6eaa804f86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -31,7 +31,7 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.Gefingerpoken import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt index 575f354c6620..f1e51e21a9e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt @@ -4,7 +4,7 @@ import android.content.Context import android.content.res.Configuration import android.util.MathUtils import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.policy.ConfigurationController import dagger.assisted.Assisted diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt index 572c0e0da262..3d574caf3192 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt @@ -8,7 +8,7 @@ import android.util.MathUtils import android.view.animation.PathInterpolator import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.qs.QS import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 77550038c94a..91c08a062b54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -52,10 +52,10 @@ import android.view.animation.Interpolator; import androidx.core.graphics.ColorUtils; +import com.android.app.animation.Interpolators; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.NotificationIconDozeHelper; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.util.drawable.DrawableSize; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 79d01b4a73fd..d6a14604ffb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -42,6 +42,7 @@ import android.view.animation.Interpolator; import androidx.annotation.NonNull; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; @@ -49,7 +50,6 @@ import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.internal.logging.UiEventLogger; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; -import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java index 2fa27ee454e8..67ab0601cbf8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java @@ -25,8 +25,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.TransformState; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index bfc4e9c47db5..eddb6835318d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -26,7 +26,7 @@ import android.view.View import android.widget.FrameLayout import com.android.internal.annotations.GuardedBy import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java index 0446165be5fc..b09b9f42e6af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java @@ -21,8 +21,8 @@ import android.util.Pools; import android.view.View; import android.widget.ImageView; +import com.android.app.animation.Interpolators; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.row.HybridNotificationView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt index c22dbf615190..785e65dd4026 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt @@ -3,7 +3,7 @@ package com.android.systemui.statusbar.notification import android.util.MathUtils import com.android.internal.annotations.VisibleForTesting import com.android.systemui.animation.ActivityLaunchAnimator -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.animation.LaunchAnimator import kotlin.math.min diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java index c22cd1ba4bc8..5a14200677b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java @@ -23,13 +23,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.android.app.animation.Interpolators; import com.android.internal.widget.IMessagingLayout; import com.android.internal.widget.MessagingGroup; import com.android.internal.widget.MessagingImageMessage; import com.android.internal.widget.MessagingLinearLayout; import com.android.internal.widget.MessagingMessage; import com.android.internal.widget.MessagingPropertyAnimator; -import com.android.systemui.animation.Interpolators; import java.util.ArrayList; import java.util.HashMap; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java index 3fc7b132f38f..a0456985e8d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java @@ -24,8 +24,8 @@ import android.graphics.ColorMatrixColorFilter; import android.view.View; import android.widget.ImageView; +import com.android.app.animation.Interpolators; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import java.util.function.Consumer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index fe0b28d16239..9ba219903272 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -21,8 +21,8 @@ import android.view.animation.Interpolator import androidx.annotation.VisibleForTesting import androidx.core.animation.ObjectAnimator import com.android.systemui.Dumpable -import com.android.systemui.animation.Interpolators -import com.android.systemui.animation.InterpolatorsAndroidX +import com.android.app.animation.Interpolators +import com.android.app.animation.InterpolatorsAndroidX import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java index 5d07cac78682..57d20246ea14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java @@ -24,7 +24,7 @@ import android.util.Property; import android.view.View; import android.view.animation.Interpolator; -import com.android.systemui.animation.Interpolators; +import com.android.app.animation.Interpolators; import com.android.systemui.statusbar.notification.stack.AnimationFilter; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ViewState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index 9f9fba437869..90eb630949fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -23,11 +23,11 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import com.android.app.animation.Interpolators; import com.android.internal.widget.MessagingImageMessage; import com.android.internal.widget.MessagingPropertyAnimator; import com.android.internal.widget.ViewClippingUtil; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.ViewTransformationHelper; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt index dc1627438490..16f1a45ba83f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt @@ -22,7 +22,7 @@ import android.animation.ValueAnimator import android.view.View import android.view.ViewGroup import com.android.systemui.R -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators /** * Class to help with fading of view groups without fading one subview diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 285dd9766169..8af488ea443d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -31,12 +31,12 @@ import android.view.View; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import com.android.app.animation.Interpolators; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.settingslib.Utils; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationUtils; 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 1dc58b59339d..ba8a5f3de45c 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 @@ -64,6 +64,7 @@ import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -72,7 +73,6 @@ import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java index b56bae12be6c..7a2bee91e972 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java @@ -45,10 +45,10 @@ import android.widget.Toast; import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.internal.logging.InstanceId; import com.android.internal.logging.InstanceIdSequence; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index 3e01dd3ab330..f0e15c27b7a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -32,9 +32,9 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.Roundable; import com.android.systemui.statusbar.notification.RoundableState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 4522e41daf91..b4bfded58e4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -966,7 +966,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder @Override public ApplicationInfo getApplicationInfo() { - ApplicationInfo applicationInfo = super.getApplicationInfo(); + ApplicationInfo applicationInfo = new ApplicationInfo(super.getApplicationInfo()); applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; return applicationInfo; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index f21db0bde59a..9bc03336c3b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -25,7 +25,7 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; -import static com.android.systemui.animation.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; import static java.lang.annotation.RetentionPolicy.SOURCE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 596bdc09efe4..047db2046529 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -33,9 +33,9 @@ import android.widget.FrameLayout; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 8a50f2f527fa..99a77550cc76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -20,7 +20,7 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; -import static com.android.systemui.animation.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; import static java.lang.annotation.RetentionPolicy.SOURCE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index bafc474d7123..5a129fccff06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -39,9 +39,9 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.FrameLayout.LayoutParams; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.AlphaOptimizedImageView; import com.android.systemui.statusbar.notification.collection.NotificationEntry; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java index 5f4c9267ee4a..d5d7f75fbaa1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java @@ -45,11 +45,11 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java index 5aaf63f8d32d..b24cec150941 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java @@ -23,8 +23,8 @@ import android.util.AttributeSet; import android.view.View; import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.animation.Interpolators; import java.util.function.Consumer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 2c59c2e8a06f..ef5e86f06ef0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -34,10 +34,10 @@ import android.widget.TextView; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.NotificationExpandButton; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.ViewTransformationHelper; import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java index 7f3381ccd38a..d73bbebe40b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java @@ -22,8 +22,8 @@ import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java index 0b435fe9dcc6..9a33a9440602 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java @@ -26,7 +26,7 @@ import android.graphics.Rect; import android.view.View; import android.view.animation.Interpolator; -import com.android.systemui.animation.Interpolators; +import com.android.app.animation.Interpolators; import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.row.ExpandableView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index edff8770479a..cf051fbc2504 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -75,6 +75,7 @@ import android.view.animation.Interpolator; import android.widget.OverScroller; import android.widget.ScrollView; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.jank.InteractionJankMonitor; @@ -86,7 +87,6 @@ import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.ExpandHelper; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index ee72943bef57..f07dd00827b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -22,9 +22,9 @@ import android.animation.ValueAnimator; import android.util.Property; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.keyguard.KeyguardSliceView; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.shared.clocks.AnimatableClockView; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarIconView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java index d07da381a186..f4605be2b9c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java @@ -26,9 +26,9 @@ import android.util.Property; import android.view.View; import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification; import com.android.systemui.statusbar.notification.PropertyAnimator; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index 9dce332985f9..459071280a1e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -33,9 +33,9 @@ import android.os.SystemClock; import android.util.Log; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 90a6d0fac7ca..561bd91b964f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -23,10 +23,10 @@ import static com.android.systemui.statusbar.notification.NotificationUtils.inte import android.content.res.Resources; import android.util.MathUtils; +import com.android.app.animation.Interpolators; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView; @@ -239,7 +239,11 @@ public class KeyguardClockPositionAlgorithm { } } - private int getExpandedPreferredClockY() { + /** + * give the static topMargin, used for lockscreen clocks to get the initial translationY + * to do counter translation + */ + public int getExpandedPreferredClockY() { if (mIsSplitShade) { return mSplitShadeTargetTopMargin; } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index 9d30cb4c4852..61c1cc82482a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -30,9 +30,9 @@ import android.widget.TextView; import androidx.annotation.StyleRes; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.keyguard.KeyguardIndication; /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 13566ef8c630..720eeba0fd4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -43,9 +43,9 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; +import com.android.app.animation.Interpolators; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 12ed71bf14d1..4d716c206908 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -37,12 +37,12 @@ import androidx.core.animation.Animator; import androidx.core.animation.AnimatorListenerAdapter; import androidx.core.animation.ValueAnimator; +import com.android.app.animation.InterpolatorsAndroidX; import com.android.keyguard.CarrierTextController; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.R; -import com.android.systemui.animation.InterpolatorsAndroidX; import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.log.LogLevel; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index 6bf54430ab38..7bc4fc3c5e47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -24,21 +24,21 @@ import android.os.SystemClock; import android.util.MathUtils; import android.util.TimeUtils; +import com.android.app.animation.Interpolators; import com.android.systemui.Dumpable; -import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.KeyguardStateController; -import java.io.PrintWriter; -import java.lang.ref.WeakReference; - import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; +import java.io.PrintWriter; +import java.lang.ref.WeakReference; + /** * Class to control all aspects about light bar changes. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index cc4f901668ab..46a2457670b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -31,9 +31,9 @@ import android.util.AttributeSet; import android.util.SparseArray; import android.view.ViewTreeObserver.OnPreDrawListener; +import com.android.app.animation.Interpolators; import com.android.internal.graphics.ColorUtils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.KeyguardAffordanceView; import java.lang.annotation.Retention; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 55dc18859c25..560ea8aae594 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -15,11 +15,11 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.collection.ArrayMap; +import com.android.app.animation.Interpolators; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.ContrastColorUtil; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 006a029de8e0..bef422ce3004 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -36,10 +36,10 @@ import android.view.animation.Interpolator; import androidx.annotation.VisibleForTesting; import androidx.collection.ArrayMap; +import com.android.app.animation.Interpolators; import com.android.internal.statusbar.StatusBarIcon; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.stack.AnimationFilter; import com.android.systemui.statusbar.notification.stack.AnimationProperties; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java index 5e5317d764fe..07a6d0a5a470 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java @@ -29,7 +29,7 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.Button; -import com.android.systemui.animation.Interpolators; +import com.android.app.animation.Interpolators; import com.android.systemui.statusbar.AlphaOptimizedImageView; public class SettingsButton extends AlphaOptimizedImageView { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index 8fa803ea3a8f..cdf66526ab63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -15,7 +15,7 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD import com.android.systemui.DejankUtils -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 620d2824acfa..5af8932859c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -40,10 +40,10 @@ import android.widget.LinearLayout; import androidx.annotation.VisibleForTesting; import androidx.core.animation.Animator; +import com.android.app.animation.Interpolators; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java index 4dd63be47735..e1ec94fada81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java @@ -24,9 +24,9 @@ import android.view.View; import androidx.core.graphics.ColorUtils; +import com.android.app.animation.Interpolators; import com.android.keyguard.KeyguardConstants; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.qs.tiles.UserDetailItemView; /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java index 928e0115287d..66b52563c0a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java @@ -31,6 +31,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.android.app.animation.Interpolators; import com.android.keyguard.KeyguardConstants; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -38,7 +39,6 @@ import com.android.keyguard.KeyguardVisibilityHelper; import com.android.keyguard.dagger.KeyguardUserSwitcherScope; import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java index 850a4b499562..363b06ab780b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java @@ -21,11 +21,11 @@ import android.util.AttributeSet; import android.util.Log; import android.view.View; +import com.android.app.animation.Interpolators; import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.keyguard.KeyguardConstants; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; -import com.android.systemui.animation.Interpolators; /** * The container for the user switcher on Keyguard. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 403a7e8116c8..e311bad9e865 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -73,6 +73,7 @@ import androidx.core.animation.AnimatorSet; import androidx.core.animation.ObjectAnimator; import androidx.core.animation.ValueAnimator; +import com.android.app.animation.InterpolatorsAndroidX; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.logging.UiEvent; @@ -80,7 +81,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.animation.InterpolatorsAndroidX; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt index b563d86f65b1..21d03386b9e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt @@ -311,7 +311,7 @@ interface SmartActionInflater { setBounds(0, 0, newIconSize, newIconSize) } // Add the action icon to the Smart Action button. - setCompoundDrawables(iconDrawable, null, null, null) + setCompoundDrawablesRelative(iconDrawable, null, null, null) val onClickListener = View.OnClickListener { onSmartActionClick(entry, smartActions, actionIndex, action) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 9e88ceb3a0d1..fb6ba8542a3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -588,15 +588,15 @@ public class SmartReplyView extends ViewGroup { } /** - * Returns the combined width of the left drawable (the action icon) and the padding between the - * drawable and the button text. + * Returns the combined width of the start drawable (the action icon) and the padding between + * the drawable and the button text. */ - private int getLeftCompoundDrawableWidthWithPadding(Button button) { - Drawable[] drawables = button.getCompoundDrawables(); - Drawable leftDrawable = drawables[0]; - if (leftDrawable == null) return 0; + private int getStartCompoundDrawableWidthWithPadding(Button button) { + Drawable[] drawables = button.getCompoundDrawablesRelative(); + Drawable startDrawable = drawables[0]; + if (startDrawable == null) return 0; - return leftDrawable.getBounds().width() + button.getCompoundDrawablePadding(); + return startDrawable.getBounds().width() + button.getCompoundDrawablePadding(); } private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) { @@ -605,8 +605,8 @@ public class SmartReplyView extends ViewGroup { // Re-measure the squeezed smart reply button. clearLayoutLineCount(button); final int widthMeasureSpec = MeasureSpec.makeMeasureSpec( - button.getPaddingLeft() + button.getPaddingRight() + textWidth - + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST); + button.getPaddingStart() + button.getPaddingEnd() + textWidth + + getStartCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST); button.measure(widthMeasureSpec, heightMeasureSpec); if (button.getLayout() == null) { Log.wtf(TAG, "Button layout is null after measure."); diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt index 16123882046c..46954b5b81ef 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt @@ -18,7 +18,7 @@ package com.android.systemui.temporarydisplay.chipbar import android.view.View import android.view.ViewGroup -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators import com.android.systemui.animation.ViewHierarchyAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.children diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt index e819f946a6d6..4fbbc8915c19 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt @@ -34,10 +34,10 @@ import android.widget.TextView import androidx.annotation.DimenRes import androidx.annotation.IdRes import androidx.annotation.VisibleForTesting +import com.android.app.animation.Interpolators import com.android.internal.widget.CachingIconView import com.android.systemui.Gefingerpoken import com.android.systemui.R -import com.android.systemui.animation.Interpolators import com.android.systemui.classifier.FalsingCollector import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription import com.android.systemui.common.shared.model.Text.Companion.loadText diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt index f0fa779ba0cf..58f22466d44d 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt +++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt @@ -51,6 +51,7 @@ class DynamicColors { Pair.create("surface_variant", MDC.surfaceVariant()), Pair.create("on_surface_variant", MDC.onSurfaceVariant()), Pair.create("outline", MDC.outline()), + Pair.create("outline_variant", MDC.outlineVariant()), Pair.create("error", MDC.error()), Pair.create("on_error", MDC.onError()), Pair.create("error_container", MDC.errorContainer()), diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 43d15bcc22b3..4b73d6190d99 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -659,6 +659,10 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { == mColorScheme.getNeutral1().getS500() && res.getColor(android.R.color.system_neutral2_500, theme) == mColorScheme.getNeutral2().getS500() + && res.getColor(android.R.color.system_outline_variant_dark, theme) + == MaterialDynamicColors.outlineVariant().getArgb(mDynamicSchemeDark) + && res.getColor(android.R.color.system_outline_variant_light, theme) + == MaterialDynamicColors.outlineVariant().getArgb(mDynamicSchemeLight) && res.getColor(android.R.color.system_primary_container_dark, theme) == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeDark) && res.getColor(android.R.color.system_primary_container_light, theme) diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto index eb23b9d7a994..d940a6b5c460 100644 --- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto +++ b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto @@ -16,8 +16,6 @@ syntax = "proto2"; -import "frameworks/base/libs/WindowManager/Shell/proto/wm_shell_trace.proto"; - package com.android.systemui.tracing; option java_multiple_files = true; @@ -25,7 +23,6 @@ option java_multiple_files = true; message SystemUiTraceProto { optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1; - optional com.android.wm.shell.WmShellTraceProto wm_shell = 2; } message EdgeBackGestureHandlerProto { diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt index 5d8029293107..db4ab7edbcf1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt @@ -19,7 +19,7 @@ package com.android.systemui.util.animation import android.animation.ValueAnimator import android.graphics.PointF import android.util.MathUtils -import com.android.systemui.animation.Interpolators +import com.android.app.animation.Interpolators /** * The fraction after which we start fading in when going from a gone widget to a visible one diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 77210b78832b..91078dc65477 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -109,6 +109,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.app.animation.Interpolators; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; @@ -119,7 +120,6 @@ import com.android.settingslib.Utils; import com.android.systemui.Dumpable; import com.android.systemui.Prefs; import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; import com.android.systemui.dump.DumpManager; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.ActivityStarter; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index e60f9b65dc1c..5144d1966222 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -362,9 +362,6 @@ public final class WMShell implements @Override public void writeToProto(SystemUiTraceProto proto) { - if (proto.wmShell == null) { - proto.wmShell = new WmShellTraceProto(); - } // Dump to WMShell proto here // TODO: Figure out how we want to synchronize while dumping to proto } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/InterpolatorsAndroidXTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/InterpolatorsAndroidXTest.kt deleted file mode 100644 index 2c680be97e95..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/InterpolatorsAndroidXTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.animation - -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import java.lang.reflect.Modifier -import junit.framework.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@SmallTest -@RunWith(JUnit4::class) -class InterpolatorsAndroidXTest : SysuiTestCase() { - - @Test - fun testInterpolatorsAndInterpolatorsAndroidXPublicMethodsAreEqual() { - assertEquals( - Interpolators::class.java.getPublicMethods(), - InterpolatorsAndroidX::class.java.getPublicMethods() - ) - } - - @Test - fun testInterpolatorsAndInterpolatorsAndroidXPublicFieldsAreEqual() { - assertEquals( - Interpolators::class.java.getPublicFields(), - InterpolatorsAndroidX::class.java.getPublicFields() - ) - } - - private fun <T> Class<T>.getPublicMethods() = - declaredMethods - .filter { Modifier.isPublic(it.modifiers) } - .map { it.toString().replace(name, "") } - .toSet() - - private fun <T> Class<T>.getPublicFields() = - fields.filter { Modifier.isPublic(it.modifiers) }.map { it.name }.toSet() -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index 6ab54a374d30..da9ceb47446a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -19,6 +19,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import com.android.app.animation.Interpolators @SmallTest @RunWith(AndroidTestingRunner::class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 6e37ee791bd2..a361bbc69ef3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -68,7 +68,6 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit -@Ignore("b/279650412") @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) @SmallTest @@ -281,6 +280,7 @@ class AuthContainerViewTest : SysuiTestCase() { assertThat(authContainer!!.parent).isNull() } + @Ignore("b/279650412") @Test fun testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() { val container = initializeFingerprintContainer( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index da71188c46c6..5536d83d8287 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -1388,7 +1388,7 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void onTouch_withNewTouchDetection_doNotPilferWhenPullingUpBouncer() + public void onTouch_withNewTouchDetection_doNotProcessTouchWhenPullingUpBouncer() throws RemoteException { final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, 0L); @@ -1427,8 +1427,10 @@ public class UdfpsControllerTest extends SysuiTestCase { mBiometricExecutor.runAllReady(); moveEvent.recycle(); - // THEN the touch is NOT pilfered - verify(mInputManager, never()).pilferPointers(any()); + // THEN the touch is NOT processed + verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), + anyBoolean()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt index 8bf32cf39963..8f6017cd2a7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt @@ -21,22 +21,23 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel +import com.android.systemui.RoboPilotTest import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl +import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.RoboPilotTest import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.SystemClock @@ -94,7 +95,8 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle mock(DismissCallbackRegistry::class.java), context, mKeyguardUpdateMonitor, - mock(KeyguardBypassController::class.java), + mock(TrustRepository::class.java), + FakeFeatureFlags(), ) mAlternateBouncerInteractor = AlternateBouncerInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index d0bfaa9bedc9..5afc4059357c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt @@ -23,8 +23,8 @@ import android.util.Log.TerribleFailureHandler import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.Interpolators import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index 5da1a846fbfd..e261982dfd18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -35,12 +35,12 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepo import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.log.FaceAuthenticationLogger import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -100,7 +100,8 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { mock(DismissCallbackRegistry::class.java), context, keyguardUpdateMonitor, - mock(KeyguardBypassController::class.java), + mock(TrustRepository::class.java), + FakeFeatureFlags(), ), AlternateBouncerInteractor( mock(StatusBarStateController::class.java), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt index 24a47b014152..eed1e739b909 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.domain.interactor -import android.os.Looper +import android.hardware.biometrics.BiometricSourceType import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.testing.TestableResources @@ -28,15 +28,17 @@ import com.android.systemui.DejankUtils import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.BouncerViewDelegate +import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever @@ -67,16 +69,23 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Mock private lateinit var mPrimaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor @Mock private lateinit var falsingCollector: FalsingCollector @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry - @Mock private lateinit var keyguardBypassController: KeyguardBypassController @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - private val mainHandler = FakeHandler(Looper.getMainLooper()) + private lateinit var mainHandler: FakeHandler private lateinit var underTest: PrimaryBouncerInteractor private lateinit var resources: TestableResources + private lateinit var trustRepository: FakeTrustRepository + private lateinit var featureFlags: FakeFeatureFlags @Before fun setUp() { MockitoAnnotations.initMocks(this) + whenever(keyguardSecurityModel.getSecurityMode(anyInt())) + .thenReturn(KeyguardSecurityModel.SecurityMode.PIN) + DejankUtils.setImmediate(true) + mainHandler = FakeHandler(android.os.Looper.getMainLooper()) + trustRepository = FakeTrustRepository() + featureFlags = FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, false) } underTest = PrimaryBouncerInteractor( repository, @@ -89,7 +98,8 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { dismissCallbackRegistry, context, keyguardUpdateMonitor, - keyguardBypassController, + trustRepository, + featureFlags, ) whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null) whenever(repository.primaryBouncerShow.value).thenReturn(false) @@ -383,6 +393,55 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { verify(repository).setSideFpsShowing(false) } + @Test + fun delayBouncerWhenFaceAuthPossible() { + mainHandler.setMode(FakeHandler.Mode.QUEUEING) + + // GIVEN bouncer should be delayed due to face auth + featureFlags.apply { set(Flags.DELAY_BOUNCER, true) } + whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true) + whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) + .thenReturn(true) + + // WHEN bouncer show is requested + underTest.show(true) + + // THEN primary show & primary showing soon aren't updated immediately + verify(repository, never()).setPrimaryShow(true) + verify(repository, never()).setPrimaryShowingSoon(false) + + // WHEN all queued messages are dispatched + mainHandler.dispatchQueuedMessages() + + // THEN primary show & primary showing soon are updated + verify(repository).setPrimaryShow(true) + verify(repository).setPrimaryShowingSoon(false) + } + + @Test + fun delayBouncerWhenActiveUnlockPossible() { + mainHandler.setMode(FakeHandler.Mode.QUEUEING) + + // GIVEN bouncer should be delayed due to active unlock + featureFlags.apply { set(Flags.DELAY_BOUNCER, true) } + trustRepository.setCurrentUserActiveUnlockAvailable(true) + whenever(keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()).thenReturn(true) + + // WHEN bouncer show is requested + underTest.show(true) + + // THEN primary show & primary showing soon were scheduled to update + verify(repository, never()).setPrimaryShow(true) + verify(repository, never()).setPrimaryShowingSoon(false) + + // WHEN all queued messages are dispatched + mainHandler.dispatchQueuedMessages() + + // THEN primary show & primary showing soon are updated + verify(repository).setPrimaryShow(true) + verify(repository).setPrimaryShowingSoon(false) + } + private fun updateSideFpsVisibilityParameters( isVisible: Boolean, sfpsEnabled: Boolean, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt index e35e97109a3f..5056b43798d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -25,9 +25,11 @@ import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.utils.os.FakeHandler @@ -37,6 +39,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest @@ -71,7 +74,8 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() { dismissCallbackRegistry, context, keyguardUpdateMonitor, - keyguardBypassController, + Mockito.mock(TrustRepository::class.java), + FakeFeatureFlags(), ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt index a5b78b74fcdf..3efe38295f3d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.keyguard.ui import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt index 0e9c99e7ba6c..e9f1ac103f87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -24,9 +24,11 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel @@ -42,6 +44,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest @@ -77,7 +80,8 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { dismissCallbackRegistry, context, keyguardUpdateMonitor, - keyguardBypassController, + Mockito.mock(TrustRepository::class.java), + FakeFeatureFlags(), ) underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt index 497777545c70..f1bbd8434b40 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt @@ -132,7 +132,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { } @Test - fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsOnlyTasksWithHostProfile() { + fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsAllTasks() { givenEnterprisePoliciesFeatureFlag(enabled = false) val tasks = @@ -147,11 +147,15 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { controller.init() + // TODO (b/263950746): Cross-profile filtering is removed for now. This should be brought + // back with the future fix verify(view) .bind( listOf( createRecentTask(taskId = 1, userId = hostUserHandle.identifier), + createRecentTask(taskId = 2, userId = otherUserHandle.identifier), createRecentTask(taskId = 3, userId = hostUserHandle.identifier), + createRecentTask(taskId = 4, userId = otherUserHandle.identifier), createRecentTask(taskId = 5, userId = hostUserHandle.identifier), ) ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java index 26aa37df0c0b..d9db60d77077 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java @@ -37,6 +37,8 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.retail.data.repository.FakeRetailModeRepository; +import com.android.systemui.retail.domain.interactor.RetailModeInteractorImpl; import com.android.systemui.settings.UserTracker; import com.android.systemui.utils.leaks.LeakCheckedTest; @@ -67,6 +69,8 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { @Mock private ActivityStarter mActivityStarter; + private FakeRetailModeRepository mRetailModeRepository; + private QSFooterViewController mController; private View mEditButton; @@ -74,6 +78,9 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { public void setup() throws Exception { MockitoAnnotations.initMocks(this); + mRetailModeRepository = new FakeRetailModeRepository(); + mRetailModeRepository.setRetailMode(false); + mEditButton = new View(mContext); injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); @@ -89,7 +96,8 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { when(mView.findViewById(android.R.id.edit)).thenReturn(mEditButton); mController = new QSFooterViewController(mView, mUserTracker, mFalsingManager, - mActivityStarter, mQSPanelController); + mActivityStarter, mQSPanelController, + new RetailModeInteractorImpl(mRetailModeRepository)); mController.init(); } @@ -132,4 +140,20 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { captor.getValue().run(); verify(mQSPanelController).showEdit(mEditButton); } + + @Test + public void testEditButton_notRetailMode_visible() { + mRetailModeRepository.setRetailMode(false); + + mController.setVisibility(View.VISIBLE); + assertThat(mEditButton.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void testEditButton_retailMode_notVisible() { + mRetailModeRepository.setRetailMode(true); + + mController.setVisibility(View.VISIBLE); + assertThat(mEditButton.getVisibility()).isEqualTo(View.GONE); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt index 50a8d2630d79..fda63ed3bede 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.QSHost import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger +import com.android.systemui.retail.data.repository.FakeRetailModeRepository import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -44,6 +45,7 @@ import org.mockito.MockitoAnnotations class TileSpecSettingsRepositoryTest : SysuiTestCase() { private lateinit var secureSettings: FakeSettings + private lateinit var retailModeRepository: FakeRetailModeRepository @Mock private lateinit var logger: QSPipelineLogger @@ -57,9 +59,12 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) secureSettings = FakeSettings() + retailModeRepository = FakeRetailModeRepository() + retailModeRepository.setRetailMode(false) with(context.orCreateTestableResources) { addOverride(R.string.quick_settings_tiles_default, DEFAULT_TILES) + addOverride(R.string.quick_settings_tiles_retail_mode, RETAIL_TILES) } underTest = @@ -67,6 +72,7 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() { secureSettings, context.resources, logger, + retailModeRepository, testDispatcher, ) } @@ -346,6 +352,26 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() { assertThat(tiles).isEqualTo("b".toTileSpecs()) } + @Test + fun retailMode_usesRetailTiles() = + testScope.runTest { + retailModeRepository.setRetailMode(true) + + val tiles by collectLastValue(underTest.tilesSpecs(0)) + + assertThat(tiles).isEqualTo(RETAIL_TILES.toTileSpecs()) + } + + @Test + fun retailMode_cannotModifyTiles() = + testScope.runTest { + retailModeRepository.setRetailMode(true) + + underTest.setTiles(0, DEFAULT_TILES.toTileSpecs()) + + assertThat(loadTilesForUser(0)).isNull() + } + private fun getDefaultTileSpecs(): List<TileSpec> { return QSHost.getDefaultSpecs(context.resources).map(TileSpec::create) } @@ -360,6 +386,7 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() { companion object { private const val DEFAULT_TILES = "a,b,c" + private const val RETAIL_TILES = "d" private const val SETTING = Settings.Secure.QS_TILES private fun String.toTileSpecs(): List<TileSpec> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt new file mode 100644 index 000000000000..d7682b20a98d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt @@ -0,0 +1,84 @@ +/* + * 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.retail.data.repository + +import android.provider.Settings +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidTestingRunner::class) +class RetailModeSettingsRepositoryTest : SysuiTestCase() { + + private val globalSettings = FakeSettings() + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + private val underTest = + RetailModeSettingsRepository( + globalSettings, + backgroundDispatcher = testDispatcher, + scope = testScope.backgroundScope, + ) + + @Test + fun retailMode_defaultFalse() = + testScope.runTest { + val value by collectLastValue(underTest.retailMode) + + assertThat(value).isFalse() + assertThat(underTest.inRetailMode).isFalse() + } + + @Test + fun retailMode_false() = + testScope.runTest { + val value by collectLastValue(underTest.retailMode) + + globalSettings.putInt(SETTING, 0) + + assertThat(value).isFalse() + assertThat(underTest.inRetailMode).isFalse() + } + + @Test + fun retailMode_true() = + testScope.runTest { + val value by collectLastValue(underTest.retailMode) + + globalSettings.putInt(SETTING, 1) + + assertThat(value).isTrue() + assertThat(underTest.inRetailMode).isTrue() + } + + companion object { + private const val SETTING = Settings.Global.DEVICE_DEMO_MODE + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt new file mode 100644 index 000000000000..8f131696f815 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.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.retail.domain.interactor + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.retail.data.repository.FakeRetailModeRepository +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class RetailModeInteractorImplTest : SysuiTestCase() { + + private val retailModeRepository = FakeRetailModeRepository() + + private val underTest = RetailModeInteractorImpl(retailModeRepository) + + @Test + fun retailMode_false() { + retailModeRepository.setRetailMode(false) + + assertThat(underTest.isInRetailMode).isFalse() + } + + @Test + fun retailMode_true() { + retailModeRepository.setRetailMode(true) + + assertThat(underTest.isInRetailMode).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java index 31a33d4ff908..cbd9dba3cdbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java @@ -30,6 +30,7 @@ import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivit import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -127,6 +128,9 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { @Before public void setUp() { + assumeFalse("Skip test: does not apply to watches", + mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); + MockitoAnnotations.initMocks(this); mMainHandler = mContext.getMainThreadHandler(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt new file mode 100644 index 000000000000..d4751c86a87f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt @@ -0,0 +1,109 @@ +/* + * 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.TestableResources +import androidx.constraintlayout.widget.ConstraintSet +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.fragments.FragmentService +import com.android.systemui.navigationbar.NavigationModeController +import com.android.systemui.recents.OverviewProxyService +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +@SmallTest +class NotificationsQSContainerControllerTest : SysuiTestCase() { + + @Mock lateinit var view: NotificationsQuickSettingsContainer + @Mock lateinit var navigationModeController: NavigationModeController + @Mock lateinit var overviewProxyService: OverviewProxyService + @Mock lateinit var shadeHeaderController: ShadeHeaderController + @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager + @Mock lateinit var fragmentService: FragmentService + + lateinit var underTest: NotificationsQSContainerController + + private lateinit var fakeResources: TestableResources + + private val delayableExecutor: DelayableExecutor = FakeExecutor(FakeSystemClock()) + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + fakeResources = TestableResources(context.resources) + + whenever(view.resources).thenReturn(fakeResources.resources) + + underTest = + NotificationsQSContainerController( + view, + navigationModeController, + overviewProxyService, + shadeHeaderController, + shadeExpansionStateManager, + fragmentService, + delayableExecutor, + ) + } + + @Test + fun testSmallScreen_updateResources_splitShadeHeightIsSet() { + with(fakeResources) { + addOverride(R.bool.config_use_large_screen_shade_header, false) + addOverride(R.dimen.qs_header_height, 1) + addOverride(R.dimen.large_screen_shade_header_height, 2) + } + + underTest.updateResources() + + val captor = ArgumentCaptor.forClass(ConstraintSet::class.java) + verify(view).applyConstraints(capture(captor)) + assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(1) + } + + @Test + fun testLargeScreen_updateResources_splitShadeHeightIsSet() { + with(fakeResources) { + addOverride(R.bool.config_use_large_screen_shade_header, true) + addOverride(R.dimen.qs_header_height, 1) + addOverride(R.dimen.large_screen_shade_header_height, 2) + } + + underTest.updateResources() + + val captor = ArgumentCaptor.forClass(ConstraintSet::class.java) + verify(view).applyConstraints(capture(captor)) + assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(2) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index 9fe75abddf9c..20da8a619100 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -33,9 +33,9 @@ import android.widget.TextView import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.Interpolators import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java index a1168f809971..f0abf2f4a24f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java @@ -32,9 +32,9 @@ import android.util.Property; import android.view.View; import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.stack.AnimationFilter; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ViewState; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index d9d8b6345fcb..3b0d5120cca3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -585,8 +585,6 @@ public class SmartReplyViewTest extends SysuiTestCase { // devices. layout.setBaselineAligned(false); - final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - // Add smart replies Button previous = null; SmartReplyView.SmartReplies smartReplies = @@ -606,11 +604,7 @@ public class SmartReplyViewTest extends SysuiTestCase { if (previous != null) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) previous.getLayoutParams(); - if (isRtl) { - lp.leftMargin = mSpacing; - } else { - lp.rightMargin = mSpacing; - } + lp.setMarginEnd(mSpacing); } layout.addView(current); previous = current; @@ -634,11 +628,7 @@ public class SmartReplyViewTest extends SysuiTestCase { if (previous != null) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) previous.getLayoutParams(); - if (isRtl) { - lp.leftMargin = mSpacing; - } else { - lp.rightMargin = mSpacing; - } + lp.setMarginEnd(mSpacing); } layout.addView(current); previous = current; @@ -937,8 +927,8 @@ public class SmartReplyViewTest extends SysuiTestCase { .collect(Collectors.toList()); Button singleLineButton = buttons.get(0); Button doubleLineButton = buttons.get(1); - Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable - Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable + Drawable singleLineDrawable = singleLineButton.getCompoundDrawablesRelative()[0]; // start + Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawablesRelative()[0]; // start assertEquals(singleLineDrawable.getBounds().width(), doubleLineDrawable.getBounds().width()); assertEquals(singleLineDrawable.getBounds().height(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt index 5b431e72e2ac..09830413bdc8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt @@ -14,6 +14,8 @@ package com.android.systemui.animation +import com.android.app.animation.Interpolators + /** A [LaunchAnimator] to be used in tests. */ fun fakeLaunchAnimator(): LaunchAnimator { return LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt index 6690de87d0a2..f0dbc60cae1c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt @@ -19,13 +19,23 @@ package com.android.systemui.keyguard.data.repository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow class FakeTrustRepository : TrustRepository { private val _isCurrentUserTrusted = MutableStateFlow(false) override val isCurrentUserTrusted: Flow<Boolean> get() = _isCurrentUserTrusted + private val _isCurrentUserActiveUnlockAvailable = MutableStateFlow(false) + override val isCurrentUserActiveUnlockAvailable: StateFlow<Boolean> = + _isCurrentUserActiveUnlockAvailable.asStateFlow() + fun setCurrentUserTrusted(trust: Boolean) { _isCurrentUserTrusted.value = trust } + + fun setCurrentUserActiveUnlockAvailable(available: Boolean) { + _isCurrentUserActiveUnlockAvailable.value = available + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/retail/data/repository/FakeRetailModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/retail/data/repository/FakeRetailModeRepository.kt new file mode 100644 index 000000000000..75b29cac1068 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/retail/data/repository/FakeRetailModeRepository.kt @@ -0,0 +1,36 @@ +/* + * 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.retail.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeRetailModeRepository : RetailModeRepository { + + private val _retailMode = MutableStateFlow(false) + override val retailMode: StateFlow<Boolean> = _retailMode.asStateFlow() + + private var _retailModeValue = false + override val inRetailMode: Boolean + get() = _retailModeValue + + fun setRetailMode(value: Boolean) { + _retailMode.value = value + _retailModeValue = value + } +} diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java index feae56e89784..b8bac61b346b 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java @@ -157,6 +157,8 @@ final class RemoteFieldClassificationService if (sDebug) { Log.d(TAG, "onSuccess Response: " + response); } + fieldClassificationServiceCallbacks + .onClassificationRequestSuccess(response); } @Override @@ -165,6 +167,8 @@ final class RemoteFieldClassificationService if (sDebug) { Log.d(TAG, "onFailure"); } + fieldClassificationServiceCallbacks + .onClassificationRequestFailure(0, null); } @Override diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 3736262e0673..9e46d739be0f 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -204,6 +204,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState RemoteFieldClassificationService.FieldClassificationServiceCallbacks { private static final String TAG = "AutofillSession"; + // This should never be true in production. This is only for local debugging. + // Otherwise it will spam logcat. + private static final boolean DBG = false; + private static final String ACTION_DELAYED_FILL = "android.service.autofill.action.DELAYED_FILL"; private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID"; @@ -1240,6 +1244,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void requestAssistStructureForPccLocked(int flags) { + if (!mClassificationState.shouldTriggerRequest()) return; + mClassificationState.updatePendingRequest(); // Get request id int requestId; // TODO(b/158623971): Update this to prevent possible overflow @@ -1571,12 +1577,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO(b/266379948): Ideally wait for PCC request to finish for a while more // (say 100ms) before proceeding further on. + if (DBG) { + Slog.d(TAG, "DBG: Initial response: " + response); + } synchronized (mLock) { response = getEffectiveFillResponse(response); if (isEmptyResponse(response)) { // Treat it as a null response. processNullResponseLocked(requestId, requestFlags); } + if (DBG) { + Slog.d(TAG, "DBG: Processed response: " + response); + } processResponseLocked(response, null, requestFlags); } } @@ -1601,12 +1613,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState DatasetComputationContainer autofillProviderContainer = new DatasetComputationContainer(); computeDatasetsForProviderAndUpdateContainer(response, autofillProviderContainer); + if (DBG) { + Slog.d(TAG, "DBG: computeDatasetsForProviderAndUpdateContainer: " + + autofillProviderContainer); + } if (!mService.getMaster().isPccClassificationEnabled()) { + if (sVerbose) { + Slog.v(TAG, "PCC classification is disabled"); + } return createShallowCopy(response, autofillProviderContainer); } synchronized (mLock) { if (mClassificationState.mState != ClassificationState.STATE_RESPONSE || mClassificationState.mLastFieldClassificationResponse == null) { + if (sVerbose) { + Slog.v(TAG, "PCC classification no last response:" + + (mClassificationState.mLastFieldClassificationResponse == null) + + " ,ineligible state=" + + (mClassificationState.mState != ClassificationState.STATE_RESPONSE)); + } return createShallowCopy(response, autofillProviderContainer); } if (!mClassificationState.processResponse()) return response; @@ -1614,11 +1639,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState boolean preferAutofillProvider = mService.getMaster().preferProviderOverPcc(); boolean shouldUseFallback = mService.getMaster().shouldUsePccFallback(); if (preferAutofillProvider && !shouldUseFallback) { + if (sVerbose) { + Slog.v(TAG, "preferAutofillProvider but no fallback"); + } return createShallowCopy(response, autofillProviderContainer); } + if (DBG) { + synchronized (mLock) { + Slog.d(TAG, "DBG: ClassificationState: " + mClassificationState); + } + } DatasetComputationContainer detectionPccContainer = new DatasetComputationContainer(); computeDatasetsForPccAndUpdateContainer(response, detectionPccContainer); + if (DBG) { + Slog.d(TAG, "DBG: computeDatasetsForPccAndUpdateContainer: " + detectionPccContainer); + } DatasetComputationContainer resultContainer; if (preferAutofillProvider) { @@ -1692,6 +1728,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillResponse. Set<Dataset> mDatasets = new LinkedHashSet<>(); ArrayMap<AutofillId, Set<Dataset>> mAutofillIdToDatasetMap = new ArrayMap<>(); + + public String toString() { + final StringBuilder builder = new StringBuilder("DatasetComputationContainer["); + if (mAutofillIds != null) { + builder.append(", autofillIds=").append(mAutofillIds); + } + if (mDatasets != null) { + builder.append(", mDatasets=").append(mDatasets); + } + if (mAutofillIdToDatasetMap != null) { + builder.append(", mAutofillIdToDatasetMap=").append(mAutofillIdToDatasetMap); + } + return builder.append(']').toString(); + } } // Adds fallback datasets to the first container. @@ -1843,7 +1893,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Dataset dataset = datasets.get(i); if (dataset.getAutofillDatatypes() == null || dataset.getAutofillDatatypes().isEmpty()) continue; - if (dataset.getFieldIds() != null && dataset.getFieldIds().size() > 0) continue; ArrayList<AutofillId> fieldIds = new ArrayList<>(); ArrayList<AutofillValue> fieldValues = new ArrayList<>(); @@ -1852,9 +1901,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ArrayList<InlinePresentation> fieldInlinePresentations = new ArrayList<>(); ArrayList<InlinePresentation> fieldInlineTooltipPresentations = new ArrayList<>(); ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>(); + Set<AutofillId> datasetAutofillIds = new ArraySet<>(); for (int j = 0; j < dataset.getAutofillDatatypes().size(); j++) { - if (dataset.getAutofillDatatypes().get(0) == null) continue; + if (dataset.getAutofillDatatypes().get(j) == null) continue; String hint = dataset.getAutofillDatatypes().get(j); if (hintsToAutofillIdMap.containsKey(hint)) { @@ -1863,6 +1913,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState for (AutofillId autofillId : tempIds) { eligibleAutofillIds.add(autofillId); + datasetAutofillIds.add(autofillId); // For each of the field, copy over values. fieldIds.add(autofillId); fieldValues.add(dataset.getFieldValues().get(j)); @@ -1876,37 +1927,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState dataset.getFieldInlineTooltipPresentation(j)); fieldFilters.add(dataset.getFilter(j)); } - - Dataset newDataset = - new Dataset( - fieldIds, - fieldValues, - fieldPresentations, - fieldDialogPresentations, - fieldInlinePresentations, - fieldInlineTooltipPresentations, - fieldFilters, - new ArrayList<>(), - dataset.getFieldContent(), - null, - null, - null, - null, - dataset.getId(), - dataset.getAuthentication()); - eligibleDatasets.add(newDataset); - - // Associate this dataset with all the ids that are represented with it. - Set<Dataset> newDatasets; - for (AutofillId autofillId : tempIds) { - if (map.containsKey(autofillId)) { - newDatasets = map.get(autofillId); - } else { - newDatasets = new ArraySet<>(); - } - newDatasets.add(newDataset); - map.put(autofillId, newDatasets); - } } // TODO(b/266379948): handle the case: // groupHintsToAutofillIdMap.containsKey(hint)) @@ -1914,6 +1934,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO(b/266379948): also handle the case where there could be more types in // the dataset, provided by the provider, however, they aren't applicable. } + Dataset newDataset = + new Dataset( + fieldIds, + fieldValues, + fieldPresentations, + fieldDialogPresentations, + fieldInlinePresentations, + fieldInlineTooltipPresentations, + fieldFilters, + new ArrayList<>(), + dataset.getFieldContent(), + null, + null, + null, + null, + dataset.getId(), + dataset.getAuthentication()); + eligibleDatasets.add(newDataset); + Set<Dataset> newDatasets; + for (AutofillId autofillId : datasetAutofillIds) { + if (map.containsKey(autofillId)) { + newDatasets = map.get(autofillId); + } else { + newDatasets = new ArraySet<>(); + } + newDatasets.add(newDataset); + map.put(autofillId, newDatasets); + } } container.mAutofillIds = eligibleAutofillIds; container.mDatasets = eligibleDatasets; @@ -5319,6 +5367,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mState = STATE_PENDING_REQUEST; mPendingFieldClassificationRequest = null; } + + @GuardedBy("mLock") + private boolean shouldTriggerRequest() { + return mState == STATE_INITIAL || mState == STATE_INVALIDATED; + } + + @GuardedBy("mLock") + @Override + public String toString() { + return "ClassificationState: [" + + "state=" + stateToString() + + ", mPendingFieldClassificationRequest=" + mPendingFieldClassificationRequest + + ", mLastFieldClassificationResponse=" + mLastFieldClassificationResponse + + ", mClassificationHintsMap=" + mClassificationHintsMap + + ", mClassificationGroupHintsMap=" + mClassificationGroupHintsMap + + ", mHintsToAutofillIdMap=" + mHintsToAutofillIdMap + + ", mGroupHintsToAutofillIdMap=" + mGroupHintsToAutofillIdMap + + "]"; + } + } @Override @@ -5843,7 +5911,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid; } - // DetectionServiceCallbacks + // FieldClassificationServiceCallbacks public void onClassificationRequestSuccess(@Nullable FieldClassificationResponse response) { mClassificationState.updateResponseReceived(response); } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java index 6f99d8677646..e436e9300fb5 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java @@ -128,9 +128,9 @@ public class CallMetadataSyncConnectionService extends ConnectionService { private static final class CallMetadataSyncConnectionIdentifier { private final int mAssociationId; - private final long mCallId; + private final String mCallId; - CallMetadataSyncConnectionIdentifier(int associationId, long callId) { + CallMetadataSyncConnectionIdentifier(int associationId, String callId) { mAssociationId = associationId; mCallId = callId; } @@ -139,7 +139,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { return mAssociationId; } - public long getCallId() { + public String getCallId() { return mCallId; } @@ -161,9 +161,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { private abstract static class CallMetadataSyncConnectionCallback { - abstract void sendCallAction(int associationId, long callId, int action); - - abstract void sendStateChange(int associationId, long callId, int newState); + abstract void sendCallAction(int associationId, String callId, int action); } private static class CallMetadataSyncConnection extends Connection { @@ -184,7 +182,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { mCallback = callback; } - public long getCallId() { + public String getCallId() { return mCall.getId(); } @@ -205,22 +203,22 @@ public class CallMetadataSyncConnectionService extends ConnectionService { } final Bundle extras = new Bundle(); - extras.putLong(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId()); + extras.putString(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId()); putExtras(extras); int capabilities = getConnectionCapabilities(); - if (mCall.hasControl(android.companion.Telecom.Call.PUT_ON_HOLD)) { + if (mCall.hasControl(android.companion.Telecom.PUT_ON_HOLD)) { capabilities |= CAPABILITY_HOLD; } else { capabilities &= ~CAPABILITY_HOLD; } - if (mCall.hasControl(android.companion.Telecom.Call.MUTE)) { + if (mCall.hasControl(android.companion.Telecom.MUTE)) { capabilities |= CAPABILITY_MUTE; } else { capabilities &= ~CAPABILITY_MUTE; } mAudioManager.setMicrophoneMute( - mCall.hasControl(android.companion.Telecom.Call.UNMUTE)); + mCall.hasControl(android.companion.Telecom.UNMUTE)); if (capabilities != getConnectionCapabilities()) { setConnectionCapabilities(capabilities); } @@ -248,8 +246,8 @@ public class CallMetadataSyncConnectionService extends ConnectionService { int capabilities = getConnectionCapabilities(); final boolean hasHoldControl = mCall.hasControl( - android.companion.Telecom.Call.PUT_ON_HOLD) - || mCall.hasControl(android.companion.Telecom.Call.TAKE_OFF_HOLD); + android.companion.Telecom.PUT_ON_HOLD) + || mCall.hasControl(android.companion.Telecom.TAKE_OFF_HOLD); if (hasHoldControl != ((getConnectionCapabilities() & CAPABILITY_HOLD) == CAPABILITY_HOLD)) { if (hasHoldControl) { @@ -258,7 +256,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { capabilities &= ~CAPABILITY_HOLD; } } - final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.Call.MUTE); + final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.MUTE); if (hasMuteControl != ((getConnectionCapabilities() & CAPABILITY_MUTE) == CAPABILITY_MUTE)) { if (hasMuteControl) { @@ -268,7 +266,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { } } mAudioManager.setMicrophoneMute( - mCall.hasControl(android.companion.Telecom.Call.UNMUTE)); + mCall.hasControl(android.companion.Telecom.UNMUTE)); if (capabilities != getConnectionCapabilities()) { setConnectionCapabilities(capabilities); } @@ -276,12 +274,12 @@ public class CallMetadataSyncConnectionService extends ConnectionService { @Override public void onAnswer(int videoState) { - sendCallAction(android.companion.Telecom.Call.ACCEPT); + sendCallAction(android.companion.Telecom.ACCEPT); } @Override public void onReject() { - sendCallAction(android.companion.Telecom.Call.REJECT); + sendCallAction(android.companion.Telecom.REJECT); } @Override @@ -296,33 +294,28 @@ public class CallMetadataSyncConnectionService extends ConnectionService { @Override public void onSilence() { - sendCallAction(android.companion.Telecom.Call.SILENCE); + sendCallAction(android.companion.Telecom.SILENCE); } @Override public void onHold() { - sendCallAction(android.companion.Telecom.Call.PUT_ON_HOLD); + sendCallAction(android.companion.Telecom.PUT_ON_HOLD); } @Override public void onUnhold() { - sendCallAction(android.companion.Telecom.Call.TAKE_OFF_HOLD); + sendCallAction(android.companion.Telecom.TAKE_OFF_HOLD); } @Override public void onMuteStateChanged(boolean isMuted) { - sendCallAction(isMuted ? android.companion.Telecom.Call.MUTE - : android.companion.Telecom.Call.UNMUTE); + sendCallAction(isMuted ? android.companion.Telecom.MUTE + : android.companion.Telecom.UNMUTE); } @Override public void onDisconnect() { - sendCallAction(android.companion.Telecom.Call.END); - } - - @Override - public void onStateChanged(int state) { - mCallback.sendStateChange(mAssociationId, mCall.getId(), state); + sendCallAction(android.companion.Telecom.END); } private void sendCallAction(int action) { diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java index 1e4bb9a504ba..5b0c745a7173 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java @@ -33,14 +33,14 @@ import java.util.Set; /** A read-only snapshot of an {@link ContextSyncMessage}. */ class CallMetadataSyncData { - final Map<Long, CallMetadataSyncData.Call> mCalls = new HashMap<>(); + final Map<String, CallMetadataSyncData.Call> mCalls = new HashMap<>(); final List<CallMetadataSyncData.Call> mRequests = new ArrayList<>(); public void addCall(CallMetadataSyncData.Call call) { mCalls.put(call.getId(), call); } - public boolean hasCall(long id) { + public boolean hasCall(String id) { return mCalls.containsKey(id); } @@ -57,7 +57,7 @@ class CallMetadataSyncData { } public static class Call implements Parcelable { - private long mId; + private String mId; private String mCallerId; private byte[] mAppIcon; private String mAppName; @@ -67,7 +67,7 @@ class CallMetadataSyncData { public static Call fromParcel(Parcel parcel) { final Call call = new Call(); - call.setId(parcel.readLong()); + call.setId(parcel.readString()); call.setCallerId(parcel.readString()); call.setAppIcon(parcel.readBlob()); call.setAppName(parcel.readString()); @@ -82,7 +82,7 @@ class CallMetadataSyncData { @Override public void writeToParcel(Parcel parcel, int parcelableFlags) { - parcel.writeLong(mId); + parcel.writeString(mId); parcel.writeString(mCallerId); parcel.writeBlob(mAppIcon); parcel.writeString(mAppName); @@ -94,7 +94,7 @@ class CallMetadataSyncData { } } - void setId(long id) { + void setId(String id) { mId = id; } @@ -122,7 +122,7 @@ class CallMetadataSyncData { mControls.add(control); } - long getId() { + String getId() { return mId; } @@ -157,7 +157,7 @@ class CallMetadataSyncData { @Override public boolean equals(Object other) { if (other instanceof CallMetadataSyncData.Call) { - return ((Call) other).getId() == getId(); + return mId != null && mId.equals(((Call) other).getId()); } return false; } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java index 443a732eb6f1..0c2373023177 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java @@ -41,7 +41,6 @@ import java.util.stream.Collectors; public class CallMetadataSyncInCallService extends InCallService { private static final String TAG = "CallMetadataIcs"; - private static final long NOT_VALID = -1L; private CompanionDeviceManagerServiceInternal mCdmsi; @@ -71,7 +70,7 @@ public class CallMetadataSyncInCallService extends InCallService { callMetadataSyncData.getRequests().iterator(); while (iterator.hasNext()) { final CallMetadataSyncData.Call call = iterator.next(); - if (call.getId() != 0) { + if (call.getId() != null) { // The call is already assigned an id; treat as control invocations. for (int control : call.getControls()) { processCallControlAction(call.getId(), control); @@ -81,41 +80,41 @@ public class CallMetadataSyncInCallService extends InCallService { } } - private void processCallControlAction(long crossDeviceCallId, + private void processCallControlAction(String crossDeviceCallId, int callControlAction) { final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId, mCurrentCalls.values()); switch (callControlAction) { - case android.companion.Telecom.Call.ACCEPT: + case android.companion.Telecom.ACCEPT: if (crossDeviceCall != null) { crossDeviceCall.doAccept(); } break; - case android.companion.Telecom.Call.REJECT: + case android.companion.Telecom.REJECT: if (crossDeviceCall != null) { crossDeviceCall.doReject(); } break; - case android.companion.Telecom.Call.SILENCE: + case android.companion.Telecom.SILENCE: doSilence(); break; - case android.companion.Telecom.Call.MUTE: + case android.companion.Telecom.MUTE: doMute(); break; - case android.companion.Telecom.Call.UNMUTE: + case android.companion.Telecom.UNMUTE: doUnmute(); break; - case android.companion.Telecom.Call.END: + case android.companion.Telecom.END: if (crossDeviceCall != null) { crossDeviceCall.doEnd(); } break; - case android.companion.Telecom.Call.PUT_ON_HOLD: + case android.companion.Telecom.PUT_ON_HOLD: if (crossDeviceCall != null) { crossDeviceCall.doPutOnHold(); } break; - case android.companion.Telecom.Call.TAKE_OFF_HOLD: + case android.companion.Telecom.TAKE_OFF_HOLD: if (crossDeviceCall != null) { crossDeviceCall.doTakeOffHold(); } @@ -171,12 +170,12 @@ public class CallMetadataSyncInCallService extends InCallService { @Nullable @VisibleForTesting - CrossDeviceCall getCallForId(long crossDeviceCallId, Collection<CrossDeviceCall> calls) { - if (crossDeviceCallId == NOT_VALID) { + CrossDeviceCall getCallForId(String crossDeviceCallId, Collection<CrossDeviceCall> calls) { + if (crossDeviceCallId == null) { return null; } for (CrossDeviceCall crossDeviceCall : calls) { - if (crossDeviceCall.getId() == crossDeviceCallId) { + if (crossDeviceCallId.equals(crossDeviceCall.getId())) { return crossDeviceCall; } } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java index ac981d45561b..168068ec6497 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java @@ -23,7 +23,6 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.telecom.Call; import android.telecom.CallAudioState; import android.telecom.VideoProfile; @@ -34,7 +33,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.ByteArrayOutputStream; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; +import java.util.UUID; /** Data holder for a telecom call and additional metadata. */ public class CrossDeviceCall { @@ -45,9 +44,7 @@ public class CrossDeviceCall { "com.android.companion.datatransfer.contextsync.extra.CALL_ID"; private static final int APP_ICON_BITMAP_DIMENSION = 256; - private static final AtomicLong sNextId = new AtomicLong(1); - - private final long mId; + private final String mId; private Call mCall; @VisibleForTesting boolean mIsEnterprise; @VisibleForTesting boolean mIsOtt; @@ -64,14 +61,14 @@ public class CrossDeviceCall { CallAudioState callAudioState) { this(packageManager, call.getDetails(), callAudioState); mCall = call; - final Bundle extras = new Bundle(); - extras.putLong(EXTRA_CALL_ID, mId); - call.putExtras(extras); + call.putExtra(EXTRA_CALL_ID, mId); } CrossDeviceCall(PackageManager packageManager, Call.Details callDetails, CallAudioState callAudioState) { - mId = sNextId.getAndIncrement(); + final String predefinedId = callDetails.getIntentExtras() != null + ? callDetails.getIntentExtras().getString(EXTRA_CALL_ID) : null; + mId = predefinedId != null ? predefinedId : UUID.randomUUID().toString(); mCallingAppPackageName = callDetails.getAccountHandle().getComponentName().getPackageName(); mIsOtt = (callDetails.getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED) @@ -145,7 +142,7 @@ public class CrossDeviceCall { if (mStatus == android.companion.Telecom.Call.RINGING) { mStatus = android.companion.Telecom.Call.RINGING_SILENCED; } - mControls.remove(android.companion.Telecom.Call.SILENCE); + mControls.remove(android.companion.Telecom.SILENCE); } @VisibleForTesting @@ -156,26 +153,26 @@ public class CrossDeviceCall { mControls.clear(); if (mStatus == android.companion.Telecom.Call.RINGING || mStatus == android.companion.Telecom.Call.RINGING_SILENCED) { - mControls.add(android.companion.Telecom.Call.ACCEPT); - mControls.add(android.companion.Telecom.Call.REJECT); + mControls.add(android.companion.Telecom.ACCEPT); + mControls.add(android.companion.Telecom.REJECT); if (mStatus == android.companion.Telecom.Call.RINGING) { - mControls.add(android.companion.Telecom.Call.SILENCE); + mControls.add(android.companion.Telecom.SILENCE); } } if (mStatus == android.companion.Telecom.Call.ONGOING || mStatus == android.companion.Telecom.Call.ON_HOLD) { - mControls.add(android.companion.Telecom.Call.END); + mControls.add(android.companion.Telecom.END); if (callDetails.can(Call.Details.CAPABILITY_HOLD)) { mControls.add( mStatus == android.companion.Telecom.Call.ON_HOLD - ? android.companion.Telecom.Call.TAKE_OFF_HOLD - : android.companion.Telecom.Call.PUT_ON_HOLD); + ? android.companion.Telecom.TAKE_OFF_HOLD + : android.companion.Telecom.PUT_ON_HOLD); } } if (mStatus == android.companion.Telecom.Call.ONGOING && callDetails.can( Call.Details.CAPABILITY_MUTE)) { - mControls.add(mIsMuted ? android.companion.Telecom.Call.UNMUTE - : android.companion.Telecom.Call.MUTE); + mControls.add(mIsMuted ? android.companion.Telecom.UNMUTE + : android.companion.Telecom.MUTE); } } @@ -212,7 +209,7 @@ public class CrossDeviceCall { } } - public long getId() { + public String getId() { return mId; } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java index adc5faf24f2c..e5ab963b6cbb 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java @@ -270,7 +270,7 @@ public class CrossDeviceSyncController { while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (pis.getFieldNumber()) { case (int) Telecom.Call.ID: - call.setId(pis.readLong(Telecom.Call.ID)); + call.setId(pis.readString(Telecom.Call.ID)); break; case (int) Telecom.Call.ORIGIN: final long originToken = pis.start(Telecom.Call.ORIGIN); @@ -336,7 +336,7 @@ public class CrossDeviceSyncController { } /** Create a call control message. */ - public static byte[] createCallControlMessage(long callId, int control) { + public static byte[] createCallControlMessage(String callId, int control) { final ProtoOutputStream pos = new ProtoOutputStream(); pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION); final long telecomToken = pos.start(ContextSyncMessage.TELECOM); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1a5d4253b5ba..a992765c0411 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16060,6 +16060,8 @@ public class ActivityManagerService extends IActivityManager.Stub final int procState = uidRec != null ? uidRec.getSetProcState() : PROCESS_STATE_NONEXISTENT; + final int procAdj = uidRec != null + ? uidRec.getMinProcAdj() : ProcessList.INVALID_ADJ; final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0; final int capability = uidRec != null ? uidRec.getSetCapability() : 0; final boolean ephemeral = uidRec != null ? uidRec.isEphemeral() : isEphemeralLocked(uid); @@ -16075,7 +16077,7 @@ public class ActivityManagerService extends IActivityManager.Stub } final int enqueuedChange = mUidObserverController.enqueueUidChange( uidRec == null ? null : uidRec.pendingChange, - uid, change, procState, procStateSeq, capability, ephemeral); + uid, change, procState, procAdj, procStateSeq, capability, ephemeral); if (uidRec != null) { uidRec.setLastReportedChange(enqueuedChange); } diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java index 5a4d315767ca..4a69f90d9fc0 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java @@ -67,7 +67,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; -import android.os.UserManager; import android.text.TextUtils; import android.util.EventLog; import android.util.IndentingPrintWriter; @@ -78,6 +77,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.os.TimeoutRecord; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; +import com.android.server.pm.UserJourneyLogger; import com.android.server.pm.UserManagerInternal; import dalvik.annotation.optimization.NeverCompile; @@ -1518,7 +1518,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { final UserInfo userInfo = (umInternal != null) ? umInternal.getUserInfo(r.userId) : null; if (userInfo != null) { - userType = UserManager.getUserTypeForStatsd(userInfo.userType); + userType = UserJourneyLogger.getUserTypeForStatsd(userInfo.userType); } Slog.i(TAG_BROADCAST, "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:" diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 438a08c44ef4..0417b8cfa2e2 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -644,6 +644,11 @@ class ProcessRecord implements WindowProcessListener { } } + @GuardedBy({"mService", "mProcLock"}) + int getSetAdj() { + return mState.getSetAdj(); + } + @GuardedBy(anyOf = {"mService", "mProcLock"}) IApplicationThread getThread() { return mThread; diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java index 51cb9878c0b3..790cc7b87f80 100644 --- a/services/core/java/com/android/server/am/UidObserverController.java +++ b/services/core/java/com/android/server/am/UidObserverController.java @@ -96,7 +96,7 @@ public class UidObserverController { } int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState, - long procStateSeq, int capability, boolean ephemeral) { + int procAdj, long procStateSeq, int capability, boolean ephemeral) { synchronized (mLock) { if (mPendingUidChanges.size() == 0) { if (DEBUG_UID_OBSERVERS) { @@ -117,6 +117,7 @@ public class UidObserverController { changeRecord.uid = uid; changeRecord.change = change; changeRecord.procState = procState; + changeRecord.procAdj = procAdj; changeRecord.procStateSeq = procStateSeq; changeRecord.capability = capability; changeRecord.ephemeral = ephemeral; @@ -344,7 +345,7 @@ public class UidObserverController { } if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROC_OOM_ADJ) != 0 && (change & UidRecord.CHANGE_PROCADJ) != 0) { - observer.onUidProcAdjChanged(item.uid); + observer.onUidProcAdjChanged(item.uid, item.procAdj); } } final int duration = (int) (SystemClock.uptimeMillis() - start); @@ -426,6 +427,7 @@ public class UidObserverController { public int uid; public int change; public int procState; + public int procAdj; public int capability; public boolean ephemeral; public long procStateSeq; @@ -435,6 +437,7 @@ public class UidObserverController { changeRecord.uid = uid; changeRecord.change = change; changeRecord.procState = procState; + changeRecord.procAdj = procAdj; changeRecord.capability = capability; changeRecord.ephemeral = ephemeral; changeRecord.procStateSeq = procStateSeq; diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index e39ac2b08479..993088ef106e 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -51,6 +51,12 @@ public final class UidRecord { private boolean mProcAdjChanged; @CompositeRWLock({"mService", "mProcLock"}) + private int mCurAdj; + + @CompositeRWLock({"mService", "mProcLock"}) + private int mSetAdj; + + @CompositeRWLock({"mService", "mProcLock"}) private int mCurCapability; @CompositeRWLock({"mService", "mProcLock"}) @@ -201,12 +207,24 @@ public final class UidRecord { mProcAdjChanged = false; } - @GuardedBy({"mService", "mProcLock"}) + @GuardedBy(anyOf = {"mService", "mProcLock"}) boolean getProcAdjChanged() { return mProcAdjChanged; } @GuardedBy(anyOf = {"mService", "mProcLock"}) + int getMinProcAdj() { + int minAdj = ProcessList.UNKNOWN_ADJ; + for (int i = mProcRecords.size() - 1; i >= 0; i--) { + int adj = mProcRecords.valueAt(i).getSetAdj(); + if (adj < minAdj) { + minAdj = adj; + } + } + return minAdj; + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) int getCurCapability() { return mCurCapability; } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b2fdee7a6f89..334c145eaf6e 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -46,13 +46,24 @@ import static com.android.server.am.UserState.STATE_BOOTING; import static com.android.server.am.UserState.STATE_RUNNING_LOCKED; import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED; import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INVALID_SESSION_ID; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_BEGIN; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_FINISH; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_NONE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_STOP; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKED_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKING_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED; import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE; import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND_VISIBLE; import static com.android.server.pm.UserManagerInternal.USER_START_MODE_FOREGROUND; import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString; import static com.android.server.pm.UserManagerInternal.userStartModeToString; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -123,6 +134,8 @@ import com.android.server.LocalServices; import com.android.server.SystemService.UserCompletedEventType; import com.android.server.SystemServiceManager; import com.android.server.am.UserState.KeyEvictedCallback; +import com.android.server.pm.UserJourneyLogger; +import com.android.server.pm.UserJourneyLogger.UserJourneySession; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerInternal.UserLifecycleListener; import com.android.server.pm.UserManagerInternal.UserStartMode; @@ -138,7 +151,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -221,75 +233,6 @@ class UserController implements Handler.Callback { // TODO(b/197344658): Increase to 10s or 15s once we have a switch-UX-is-done invocation too. private static final int USER_COMPLETED_EVENT_DELAY_MS = 5 * 1000; - // Used for statsd logging with UserLifecycleJourneyReported + UserLifecycleEventOccurred atoms - private static final long INVALID_SESSION_ID = 0; - - // The various user journeys, defined in the UserLifecycleJourneyReported atom for statsd - private static final int USER_JOURNEY_UNKNOWN = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__UNKNOWN; - private static final int USER_JOURNEY_USER_SWITCH_FG = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_FG; - private static final int USER_JOURNEY_USER_SWITCH_UI = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_UI; - private static final int USER_JOURNEY_USER_START = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START; - private static final int USER_JOURNEY_USER_CREATE = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE; - private static final int USER_JOURNEY_USER_STOP = - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP; - @IntDef(prefix = { "USER_JOURNEY" }, value = { - USER_JOURNEY_UNKNOWN, - USER_JOURNEY_USER_SWITCH_FG, - USER_JOURNEY_USER_SWITCH_UI, - USER_JOURNEY_USER_START, - USER_JOURNEY_USER_CREATE, - USER_JOURNEY_USER_STOP - }) - @interface UserJourney {} - - // The various user lifecycle events, defined in the UserLifecycleEventOccurred atom for statsd - private static final int USER_LIFECYCLE_EVENT_UNKNOWN = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN; - private static final int USER_LIFECYCLE_EVENT_SWITCH_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__SWITCH_USER; - private static final int USER_LIFECYCLE_EVENT_START_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__START_USER; - private static final int USER_LIFECYCLE_EVENT_CREATE_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; - private static final int USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__USER_RUNNING_LOCKED; - private static final int USER_LIFECYCLE_EVENT_UNLOCKING_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER; - private static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER; - private static final int USER_LIFECYCLE_EVENT_STOP_USER = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER; - @IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = { - USER_LIFECYCLE_EVENT_UNKNOWN, - USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_START_USER, - USER_LIFECYCLE_EVENT_CREATE_USER, - USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, - USER_LIFECYCLE_EVENT_UNLOCKING_USER, - USER_LIFECYCLE_EVENT_UNLOCKED_USER, - USER_LIFECYCLE_EVENT_STOP_USER - }) - @interface UserLifecycleEvent {} - - // User lifecyle event state, defined in the UserLifecycleEventOccurred atom for statsd - private static final int USER_LIFECYCLE_EVENT_STATE_BEGIN = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN; - private static final int USER_LIFECYCLE_EVENT_STATE_FINISH = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH; - private static final int USER_LIFECYCLE_EVENT_STATE_NONE = - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE; - @IntDef(prefix = { "USER_LIFECYCLE_EVENT_STATE" }, value = { - USER_LIFECYCLE_EVENT_STATE_BEGIN, - USER_LIFECYCLE_EVENT_STATE_FINISH, - USER_LIFECYCLE_EVENT_STATE_NONE, - }) - @interface UserLifecycleEventState {} - /** * Maximum number of users we allow to be running at a time, including system user. * @@ -420,13 +363,6 @@ class UserController implements Handler.Callback { private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>(); /** - * {@link UserIdInt} to {@link UserJourneySession} mapping used for statsd logging for the - * UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms. - */ - @GuardedBy("mUserIdToUserJourneyMap") - private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>(); - - /** * Map of userId to {@link UserCompletedEventType} event flags, indicating which as-yet- * unreported user-starting events have transpired for the given user. */ @@ -621,8 +557,9 @@ class UserController implements Handler.Callback { // but we might immediately step into RUNNING below if the user // storage is already unlocked. if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) { - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, - USER_LIFECYCLE_EVENT_STATE_NONE); + mInjector.getUserJourneyLogger() + .logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + EVENT_STATE_NONE); mInjector.getUserManagerInternal().setUserState(userId, uss.state); // Do not report secondary users, runtime restarts or first boot/upgrade if (userId == UserHandle.USER_SYSTEM @@ -646,7 +583,10 @@ class UserController implements Handler.Callback { // user transitions to RUNNING_LOCKED. However, in "headless system user mode", the // system user is explicitly started before the device has finished booting. In // that case, we need to wait until onBootComplete() to send the broadcast. - if (!(mInjector.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) { + // Similarly, this occurs after a user switch, but in HSUM we switch to the main + // user before boot is complete, so again this should be delayed until + // onBootComplete if boot has not yet completed. + if (mAllowUserUnlocking) { // ACTION_LOCKED_BOOT_COMPLETED sendLockedBootCompletedBroadcast(resultTo, userId); } @@ -694,8 +634,9 @@ class UserController implements Handler.Callback { private boolean finishUserUnlocking(final UserState uss) { final int userId = uss.mHandle.getIdentifier(); EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKING, userId); - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + mInjector.getUserJourneyLogger() + .logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER, + EVENT_STATE_BEGIN); // If the user key hasn't been unlocked yet, we cannot proceed. if (!StorageManager.isUserKeyUnlocked(userId)) return false; synchronized (mLock) { @@ -1073,9 +1014,7 @@ class UserController implements Handler.Callback { return; } - logUserJourneyInfo(null, getUserInfo(userId), USER_JOURNEY_USER_STOP); - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(userId, USER_JOURNEY_USER_STOP); if (stopUserCallback != null) { uss.mStopCallbacks.add(stopUserCallback); @@ -1138,9 +1077,16 @@ class UserController implements Handler.Callback { synchronized (mLock) { if (uss.state != UserState.STATE_STOPPING) { // Whoops, we are being started back up. Abort, abort! - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_NONE); - clearSessionId(userId); + UserJourneySession session = mInjector.getUserJourneyLogger() + .logUserJourneyFinishWithError(-1, getUserInfo(userId), + USER_JOURNEY_USER_STOP, ERROR_CODE_ABORTED); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } else { + mInjector.getUserJourneyLogger() + .logUserJourneyFinishWithError(-1, getUserInfo(userId), + USER_JOURNEY_USER_STOP, ERROR_CODE_INVALID_SESSION_ID); + } return; } uss.setState(UserState.STATE_SHUTDOWN); @@ -1247,9 +1193,11 @@ class UserController implements Handler.Callback { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - clearSessionId(userId); + UserJourneySession session = mInjector.getUserJourneyLogger() + .logUserJourneyFinish(-1, userInfo, USER_JOURNEY_USER_STOP); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } if (lockUser) { dispatchUserLocking(userIdToLock, keyEvictedCallbacks); @@ -1259,9 +1207,11 @@ class UserController implements Handler.Callback { // which was paused while the SHUTDOWN flow of the user was in progress. resumePendingUserStarts(userId); } else { - logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, - USER_LIFECYCLE_EVENT_STATE_NONE); - clearSessionId(userId); + UserJourneySession session = mInjector.getUserJourneyLogger() + .finishAndClearIncompleteUserJourney(userId, USER_JOURNEY_USER_STOP); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } } } @@ -2617,9 +2567,9 @@ class UserController implements Handler.Callback { // we should *not* transition users out of the BOOTING state using finishUserBoot(), as that // doesn't handle issuing the needed onUserStarting() call, and it would just race with an // explicit start anyway. We do, however, need to send the "locked boot complete" broadcast - // for the system user, as that got skipped earlier due to the *device* boot not being - // complete yet. We also need to try to unlock all started users, since until now explicit - // user starts didn't proceed to unlocking, due to it being too early in the device boot. + // as that got skipped earlier due to the *device* boot not being complete yet. + // We also need to try to unlock all started users, since until now explicit user starts + // didn't proceed to unlocking, due to it being too early in the device boot. // // USER_SYSTEM must be processed first. It will be first in the array, as its ID is lowest. Preconditions.checkArgument(startedUsers.keyAt(0) == UserHandle.USER_SYSTEM); @@ -2629,9 +2579,7 @@ class UserController implements Handler.Callback { if (!mInjector.isHeadlessSystemUserMode()) { finishUserBoot(uss, resultTo); } else { - if (userId == UserHandle.USER_SYSTEM) { - sendLockedBootCompletedBroadcast(resultTo, userId); - } + sendLockedBootCompletedBroadcast(resultTo, userId); maybeUnlockUser(userId); } } @@ -3135,10 +3083,7 @@ class UserController implements Handler.Callback { public boolean handleMessage(Message msg) { switch (msg.what) { case START_USER_SWITCH_FG_MSG: - logUserJourneyInfo(getUserInfo(getCurrentUserId()), getUserInfo(msg.arg1), - USER_JOURNEY_USER_SWITCH_FG); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_SWITCH_FG); startUserInForeground(msg.arg1); break; case REPORT_USER_SWITCH_MSG: @@ -3160,18 +3105,15 @@ class UserController implements Handler.Callback { mInjector.batteryStatsServiceNoteEvent( BatteryStats.HistoryItem.EVENT_USER_RUNNING_START, Integer.toString(msg.arg1), msg.arg1); - logUserJourneyInfo(null, getUserInfo(msg.arg1), USER_JOURNEY_USER_START); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_START); mInjector.onUserStarting(/* userId= */ msg.arg1); scheduleOnUserCompletedEvent(msg.arg1, UserCompletedEventType.EVENT_TYPE_USER_STARTING, USER_COMPLETED_EVENT_DELAY_MS); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - clearSessionId(msg.arg1, USER_JOURNEY_USER_START); + mInjector.getUserJourneyLogger().logUserJourneyFinish(-1 , getUserInfo(msg.arg1), + USER_JOURNEY_USER_START); break; case USER_UNLOCK_MSG: final int userId = msg.arg1; @@ -3180,10 +3122,11 @@ class UserController implements Handler.Callback { FgThread.getHandler().post(() -> { mInjector.loadUserRecents(userId); }); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKING_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + + mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1, + USER_LIFECYCLE_EVENT_UNLOCKING_USER, EVENT_STATE_FINISH); + mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1, + USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_BEGIN); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("finishUserUnlocked-" + userId); @@ -3199,9 +3142,9 @@ class UserController implements Handler.Callback { // (No need to acquire lock to read mCurrentUserId since it is volatile.) // TODO: Find something to wait for in the case of a profile. mCurrentUserId == msg.arg1 ? USER_COMPLETED_EVENT_DELAY_MS : 1000); - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); - clearSessionId(msg.arg1); + mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1, + USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_FINISH); + // Unlocking user is not a journey no need to clear sessionId break; case USER_CURRENT_MSG: mInjector.batteryStatsServiceNoteEvent( @@ -3224,22 +3167,24 @@ class UserController implements Handler.Callback { break; case REPORT_USER_SWITCH_COMPLETE_MSG: dispatchUserSwitchComplete(msg.arg1, msg.arg2); - logUserLifecycleEvent(msg.arg2, USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_STATE_FINISH); + UserJourneySession session = mInjector.getUserJourneyLogger() + .logUserSwitchJourneyFinish(msg.arg1, getUserInfo(msg.arg2)); + if (session != null) { + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session); + } break; case REPORT_LOCKED_BOOT_COMPLETE_MSG: dispatchLockedBootComplete(msg.arg1); break; case START_USER_SWITCH_UI_MSG: final Pair<UserInfo, UserInfo> fromToUserPair = (Pair<UserInfo, UserInfo>) msg.obj; - logUserJourneyInfo(fromToUserPair.first, fromToUserPair.second, - USER_JOURNEY_USER_SWITCH_UI); - logUserLifecycleEvent(fromToUserPair.second.id, USER_LIFECYCLE_EVENT_SWITCH_USER, - USER_LIFECYCLE_EVENT_STATE_BEGIN); + logUserJourneyBegin(fromToUserPair.second.id, USER_JOURNEY_USER_SWITCH_UI); showUserSwitchDialog(fromToUserPair); break; case CLEAR_USER_JOURNEY_SESSION_MSG: - logAndClearSessionId(msg.arg1); + mInjector.getUserJourneyLogger() + .finishAndClearIncompleteUserJourney(msg.arg1, msg.arg2); + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, msg.obj); break; case COMPLETE_USER_SWITCH_MSG: completeUserSwitch(msg.arg1, msg.arg2); @@ -3317,123 +3262,29 @@ class UserController implements Handler.Callback { * statsd helper method for logging the start of a user journey via a UserLifecycleEventOccurred * atom given the originating and targeting users for the journey. */ - private void logUserJourneyInfo(UserInfo origin, UserInfo target, @UserJourney int journey) { - final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); - synchronized (mUserIdToUserJourneyMap) { - UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(target.id); - if (userJourneySession != null) { - // TODO(b/157007231): Move this logic to a separate class/file. - if ((userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_UI - || userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG) - && (journey == USER_JOURNEY_USER_START - || journey == USER_JOURNEY_USER_STOP)) { - /* - * There is already a user switch journey, and a user start or stop journey for - * the same target user received. New journey is most likely a part of user - * switch journey so no need to create a new journey. - */ - if (DEBUG_MU) { - Slogf.d(TAG, journey + " not logged as it is expected to be part of " - + userJourneySession.mJourney); - } - return; - } - /* - * Possible reasons for this condition to be true: - * - A user switch journey is received while another user switch journey is in - * process for the same user. - * - A user switch journey is received while user start journey is in process for - * the same user. - * - A user start journey is received while another user start journey is in process - * for the same user. - * In all cases potentially an incomplete, timed-out session or multiple - * simultaneous requests. It is not possible to keep track of multiple sessions for - * the same user, so previous session is abandoned. - */ - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, - userJourneySession.mSessionId, target.id, USER_LIFECYCLE_EVENT_UNKNOWN, - USER_LIFECYCLE_EVENT_STATE_NONE); - } - + private void logUserJourneyBegin(int targetId, + @UserJourneyLogger.UserJourney int journey) { + UserJourneySession oldSession = mInjector.getUserJourneyLogger() + .finishAndClearIncompleteUserJourney(targetId, journey); + if (oldSession != null) { if (DEBUG_MU) { Slogf.d(TAG, - "Starting a new journey: " + journey + " with session id: " + newSessionId); + "Starting a new journey: " + journey + " with session id: " + + oldSession); } - - userJourneySession = new UserJourneySession(newSessionId, journey); - mUserIdToUserJourneyMap.put(target.id, userJourneySession); /* - * User lifecyle journey would be complete when {@code #clearSessionId} is called after - * the last expected lifecycle event for the journey. It may be possible that the last - * event is not called, e.g., user not unlocked after user switching. In such cases user - * journey is cleared after {@link USER_JOURNEY_TIMEOUT}. + * User lifecycle journey would be complete when {@code #clearSessionId} is called + * after the last expected lifecycle event for the journey. It may be possible that + * the last event is not called, e.g., user not unlocked after user switching. In such + * cases user journey is cleared after {@link USER_JOURNEY_TIMEOUT}. */ - mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG); - mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG, - target.id, /* arg2= */ 0), USER_JOURNEY_TIMEOUT_MS); - } - - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, newSessionId, - journey, origin != null ? origin.id : -1, - target.id, UserManager.getUserTypeForStatsd(target.userType), target.flags); - } - - /** - * statsd helper method for logging the given event for the UserLifecycleEventOccurred statsd - * atom. - */ - private void logUserLifecycleEvent(@UserIdInt int userId, @UserLifecycleEvent int event, - @UserLifecycleEventState int eventState) { - final long sessionId; - synchronized (mUserIdToUserJourneyMap) { - final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId); - if (userJourneySession == null || userJourneySession.mSessionId == INVALID_SESSION_ID) { - Slogf.w(TAG, "UserLifecycleEvent " + event - + " received without an active userJourneySession."); - return; - } - sessionId = userJourneySession.mSessionId; + mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, oldSession); } + UserJourneySession newSession = mInjector.getUserJourneyLogger() + .logUserJourneyBegin(targetId, journey); + mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG, + targetId, /* arg2= */ journey, newSession), USER_JOURNEY_TIMEOUT_MS); - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, eventState); - } - - /** - * Clears the {@link UserJourneySession} for a given {@link UserIdInt} and {@link UserJourney}. - */ - private void clearSessionId(@UserIdInt int userId, @UserJourney int journey) { - synchronized (mUserIdToUserJourneyMap) { - final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId); - if (userJourneySession != null && userJourneySession.mJourney == journey) { - clearSessionId(userId); - } - } - } - - /** - * Clears the {@link UserJourneySession} for a given {@link UserIdInt}. - */ - private void clearSessionId(@UserIdInt int userId) { - synchronized (mUserIdToUserJourneyMap) { - mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG); - mUserIdToUserJourneyMap.delete(userId); - } - } - - /** - * Log a final event of the {@link UserJourneySession} and clear it. - */ - private void logAndClearSessionId(@UserIdInt int userId) { - synchronized (mUserIdToUserJourneyMap) { - final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId); - if (userJourneySession != null) { - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, - userJourneySession.mSessionId, userId, USER_LIFECYCLE_EVENT_UNKNOWN, - USER_LIFECYCLE_EVENT_STATE_NONE); - } - clearSessionId(userId); - } } private BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( @@ -3471,23 +3322,6 @@ class UserController implements Handler.Callback { return mLastUserUnlockingUptime; } - /** - * Helper class to store user journey and session id. - * - * <p> User journey tracks a chain of user lifecycle events occurring during different user - * activities such as user start, user switch, and user creation. - */ - // TODO(b/157007231): Move this class and user journey tracking logic to a separate file. - private static class UserJourneySession { - final long mSessionId; - @UserJourney final int mJourney; - - UserJourneySession(long sessionId, @UserJourney int journey) { - mJourney = journey; - mSessionId = sessionId; - } - } - private static class UserProgressListener extends IProgressListener.Stub { private volatile long mUnlockStarted; @Override @@ -3562,6 +3396,10 @@ class UserController implements Handler.Callback { return new Handler(mService.mUiHandler.getLooper(), callback); } + protected UserJourneyLogger getUserJourneyLogger() { + return getUserManager().getUserJourneyLogger(); + } + protected Context getContext() { return mService.mContext; } diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index 412fbe797758..4e7865c2d7a6 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -33,6 +33,8 @@ import android.graphics.Shader; import android.graphics.drawable.Animatable2; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; @@ -53,6 +55,8 @@ import com.android.internal.util.ObjectUtils; import com.android.internal.util.UserIcons; import com.android.server.wm.WindowManagerService; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Dialog to show during the user switch. This dialog shows target user's name and their profile * picture with a circular spinner animation around it if the animations for this dialog are not @@ -64,9 +68,14 @@ class UserSwitchingDialog extends Dialog { // User switching doesn't happen that frequently, so it doesn't hurt to have it always on protected static final boolean DEBUG = true; + private static final long DIALOG_SHOW_HIDE_ANIMATION_DURATION_MS = 300; private final boolean mDisableAnimations; + // Time to wait for the onAnimationEnd() callbacks before moving on + private static final int ANIMATION_TIMEOUT_MS = 1000; + private final Handler mHandler = new Handler(Looper.myLooper()); + protected final UserInfo mOldUser; protected final UserInfo mNewUser; private final String mSwitchingFromSystemUserMessage; @@ -180,7 +189,7 @@ class UserSwitchingDialog extends Dialog { @Override public void show() { - asyncTraceBegin("", 0); + asyncTraceBegin("dialog", 0); super.show(); } @@ -188,7 +197,7 @@ class UserSwitchingDialog extends Dialog { public void dismiss() { super.dismiss(); stopFreezingScreen(); - asyncTraceEnd("", 0); + asyncTraceEnd("dialog", 0); } public void show(@NonNull Runnable onShown) { @@ -217,20 +226,18 @@ class UserSwitchingDialog extends Dialog { if (!mNeedToFreezeScreen) { return; } - if (DEBUG) Slog.d(TAG, "startFreezingScreen"); - Trace.traceBegin(TRACE_TAG, "startFreezingScreen"); + traceBegin("startFreezingScreen"); mWindowManager.startFreezingScreen(0, 0); - Trace.traceEnd(TRACE_TAG); + traceEnd("startFreezingScreen"); } private void stopFreezingScreen() { if (!mNeedToFreezeScreen) { return; } - if (DEBUG) Slog.d(TAG, "stopFreezingScreen"); - Trace.traceBegin(TRACE_TAG, "stopFreezingScreen"); + traceBegin("stopFreezingScreen"); mWindowManager.stopFreezingScreen(); - Trace.traceEnd(TRACE_TAG); + traceEnd("stopFreezingScreen"); } private void startShowAnimation(Runnable onAnimationEnd) { @@ -238,13 +245,13 @@ class UserSwitchingDialog extends Dialog { onAnimationEnd.run(); return; } - asyncTraceBegin("-showAnimation", 1); - startDialogAnimation(new AlphaAnimation(0, 1), () -> { - asyncTraceEnd("-showAnimation", 1); + asyncTraceBegin("showAnimation", 1); + startDialogAnimation("show", new AlphaAnimation(0, 1), () -> { + asyncTraceEnd("showAnimation", 1); - asyncTraceBegin("-spinnerAnimation", 2); + asyncTraceBegin("spinnerAnimation", 2); startProgressAnimation(() -> { - asyncTraceEnd("-spinnerAnimation", 2); + asyncTraceEnd("spinnerAnimation", 2); onAnimationEnd.run(); }); @@ -257,9 +264,9 @@ class UserSwitchingDialog extends Dialog { onAnimationEnd.run(); return; } - asyncTraceBegin("-dismissAnimation", 3); - startDialogAnimation(new AlphaAnimation(1, 0), () -> { - asyncTraceEnd("-dismissAnimation", 3); + asyncTraceBegin("dismissAnimation", 3); + startDialogAnimation("dismiss", new AlphaAnimation(1, 0), () -> { + asyncTraceEnd("dismissAnimation", 3); onAnimationEnd.run(); }); @@ -271,10 +278,11 @@ class UserSwitchingDialog extends Dialog { onAnimationEnd.run(); return; } + final Runnable onAnimationEndWithTimeout = animationWithTimeout("spinner", onAnimationEnd); avd.registerAnimationCallback(new Animatable2.AnimationCallback() { @Override public void onAnimationEnd(Drawable drawable) { - onAnimationEnd.run(); + onAnimationEndWithTimeout.run(); } }); avd.start(); @@ -291,12 +299,13 @@ class UserSwitchingDialog extends Dialog { return null; } - private void startDialogAnimation(Animation animation, Runnable onAnimationEnd) { + private void startDialogAnimation(String name, Animation animation, Runnable onAnimationEnd) { final View view = findViewById(R.id.content); if (mDisableAnimations || view == null) { onAnimationEnd.run(); return; } + final Runnable onAnimationEndWithTimeout = animationWithTimeout(name, onAnimationEnd); animation.setDuration(DIALOG_SHOW_HIDE_ANIMATION_DURATION_MS); animation.setAnimationListener(new Animation.AnimationListener() { @Override @@ -306,7 +315,7 @@ class UserSwitchingDialog extends Dialog { @Override public void onAnimationEnd(Animation animation) { - onAnimationEnd.run(); + onAnimationEndWithTimeout.run(); } @Override @@ -317,11 +326,39 @@ class UserSwitchingDialog extends Dialog { view.startAnimation(animation); } + private Runnable animationWithTimeout(String name, Runnable onAnimationEnd) { + final AtomicBoolean isFirst = new AtomicBoolean(true); + final Runnable onAnimationEndOrTimeout = () -> { + if (isFirst.getAndSet(false)) { + mHandler.removeCallbacksAndMessages(null); + onAnimationEnd.run(); + } + }; + mHandler.postDelayed(() -> { + Slog.w(TAG, name + " animation not completed in " + ANIMATION_TIMEOUT_MS + " ms"); + onAnimationEndOrTimeout.run(); + }, ANIMATION_TIMEOUT_MS); + + return onAnimationEndOrTimeout; + } + private void asyncTraceBegin(String subTag, int subCookie) { + if (DEBUG) Slog.d(TAG, "asyncTraceBegin-" + subTag); Trace.asyncTraceBegin(TRACE_TAG, TAG + subTag, mTraceCookie + subCookie); } private void asyncTraceEnd(String subTag, int subCookie) { Trace.asyncTraceEnd(TRACE_TAG, TAG + subTag, mTraceCookie + subCookie); + if (DEBUG) Slog.d(TAG, "asyncTraceEnd-" + subTag); + } + + private void traceBegin(String msg) { + if (DEBUG) Slog.d(TAG, "traceBegin-" + msg); + Trace.traceBegin(TRACE_TAG, msg); + } + + private void traceEnd(String msg) { + Trace.traceEnd(TRACE_TAG); + if (DEBUG) Slog.d(TAG, "traceEnd-" + msg); } } diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index d369af608d31..e4a5a3e0ed00 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -402,11 +402,15 @@ public final class GameManagerService extends IGameManagerService.Stub { public enum FrameRate { FPS_DEFAULT(0), FPS_30(30), + FPS_36(36), FPS_40(40), FPS_45(45), + FPS_48(48), FPS_60(60), + FPS_72(72), FPS_90(90), FPS_120(120), + FPS_144(144), FPS_INVALID(-1); public final int fps; @@ -423,16 +427,24 @@ public final class GameManagerService extends IGameManagerService.Stub { switch (raw) { case "30": return FrameRate.FPS_30.fps; + case "36": + return FrameRate.FPS_36.fps; case "40": return FrameRate.FPS_40.fps; case "45": return FrameRate.FPS_45.fps; + case "48": + return FrameRate.FPS_48.fps; case "60": return FrameRate.FPS_60.fps; + case "72": + return FrameRate.FPS_72.fps; case "90": return FrameRate.FPS_90.fps; case "120": return FrameRate.FPS_120.fps; + case "144": + return FrameRate.FPS_144.fps; case "disable": case "": return FrameRate.FPS_DEFAULT.fps; diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 9429b4c129f5..4984f125b00f 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -303,8 +303,6 @@ public class SoundDoseHelper { SAFE_MEDIA_VOLUME_UNINITIALIZED); mSafeMediaVolumeDevices.append(AudioSystem.DEVICE_OUT_BLE_BROADCAST, SAFE_MEDIA_VOLUME_UNINITIALIZED); - mSafeMediaVolumeDevices.append(AudioSystem.DEVICE_OUT_HEARING_AID, - SAFE_MEDIA_VOLUME_UNINITIALIZED); mSafeMediaVolumeDevices.append(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, SAFE_MEDIA_VOLUME_UNINITIALIZED); // TODO(b/278265907): enable A2DP when we can distinguish A2DP headsets diff --git a/services/core/java/com/android/server/display/color/CctEvaluator.java b/services/core/java/com/android/server/display/color/CctEvaluator.java new file mode 100644 index 000000000000..878f7e5116b8 --- /dev/null +++ b/services/core/java/com/android/server/display/color/CctEvaluator.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.color; + +import android.animation.TypeEvaluator; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Arrays; + +/** + * Interpolates between CCT values by a given step. + */ +class CctEvaluator implements TypeEvaluator<Integer> { + + private static final String TAG = "CctEvaluator"; + + /** + * The minimum input value, which will represent index 0 in the mValues array. Each + * subsequent input value is offset by this amount. + */ + private final int mIndexOffset; + /** + * Cached step values at each CCT value (offset by the {@link #mIndexOffset} above). For + * example, if the minimum CCT is 2000K (which is set to mIndexOffset), then the 0th index of + * this array is equivalent to the step value at 2000K, 1st index corresponds to 2001K, and so + * on. + */ + @VisibleForTesting + final int[] mStepsAtOffsetCcts; + /** + * Pre-computed stepped CCTs. These will be accessed frequently; the memory cost of caching them + * is well-spent. + */ + @VisibleForTesting + final int[] mSteppedCctsAtOffsetCcts; + + CctEvaluator(int min, int max, int[] cctRangeMinimums, int[] steps) { + final int delta = max - min + 1; + mStepsAtOffsetCcts = new int[delta]; + mSteppedCctsAtOffsetCcts = new int[delta]; + mIndexOffset = min; + + final int parallelArraysLength = cctRangeMinimums.length; + if (cctRangeMinimums.length != steps.length) { + Slog.e(TAG, + "Parallel arrays cctRangeMinimums and steps are different lengths; setting " + + "step of 1"); + setStepOfOne(); + } else if (parallelArraysLength == 0) { + Slog.e(TAG, "No cctRangeMinimums or steps are set; setting step of 1"); + setStepOfOne(); + } else { + int parallelArraysIndex = 0; + int index = 0; + int lastSteppedCct = Integer.MIN_VALUE; + while (index < delta) { + final int cct = index + mIndexOffset; + int nextParallelArraysIndex = parallelArraysIndex + 1; + while (nextParallelArraysIndex < parallelArraysLength + && cct >= cctRangeMinimums[nextParallelArraysIndex]) { + parallelArraysIndex = nextParallelArraysIndex; + nextParallelArraysIndex++; + } + mStepsAtOffsetCcts[index] = steps[parallelArraysIndex]; + if (lastSteppedCct == Integer.MIN_VALUE + || Math.abs(lastSteppedCct - cct) >= steps[parallelArraysIndex]) { + lastSteppedCct = cct; + } + mSteppedCctsAtOffsetCcts[index] = lastSteppedCct; + index++; + } + } + } + + @Override + public Integer evaluate(float fraction, Integer startValue, Integer endValue) { + final int cct = (int) (startValue + fraction * (endValue - startValue)); + final int index = cct - mIndexOffset; + if (index < 0 || index >= mSteppedCctsAtOffsetCcts.length) { + Slog.e(TAG, "steppedCctValueAt: returning same since invalid requested index=" + index); + return cct; + } + return mSteppedCctsAtOffsetCcts[index]; + } + + private void setStepOfOne() { + Arrays.fill(mStepsAtOffsetCcts, 1); + for (int i = 0; i < mSteppedCctsAtOffsetCcts.length; i++) { + mSteppedCctsAtOffsetCcts[i] = mIndexOffset + i; + } + } +} diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 0284d9c68689..c0ea5fead9a5 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -180,6 +180,8 @@ public final class ColorDisplayService extends SystemService { */ private SparseIntArray mColorModeCompositionColorSpaces = null; + private final Object mCctTintApplierLock = new Object(); + public ColorDisplayService(Context context) { super(context); mHandler = new TintHandler(DisplayThread.get().getLooper()); @@ -698,6 +700,79 @@ public final class ColorDisplayService extends SystemService { } } + private void applyTintByCct(ColorTemperatureTintController tintController, boolean immediate) { + synchronized (mCctTintApplierLock) { + tintController.cancelAnimator(); + + final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); + final int from = tintController.getAppliedCct(); + final int to = tintController.isActivated() ? tintController.getTargetCct() + : tintController.getDisabledCct(); + + if (immediate) { + Slog.d(TAG, tintController.getClass().getSimpleName() + + " applied immediately: toCct=" + to + " fromCct=" + from); + dtm.setColorMatrix(tintController.getLevel(), + tintController.computeMatrixForCct(to)); + tintController.setAppliedCct(to); + } else { + Slog.d(TAG, tintController.getClass().getSimpleName() + " animation started: toCct=" + + to + " fromCct=" + from); + ValueAnimator valueAnimator = ValueAnimator.ofInt(from, to); + tintController.setAnimator(valueAnimator); + final CctEvaluator evaluator = tintController.getEvaluator(); + if (evaluator != null) { + valueAnimator.setEvaluator(evaluator); + } + valueAnimator.setDuration(tintController.getTransitionDurationMilliseconds()); + valueAnimator.setInterpolator(AnimationUtils.loadInterpolator( + getContext(), android.R.interpolator.linear)); + valueAnimator.addUpdateListener((ValueAnimator animator) -> { + synchronized (mCctTintApplierLock) { + final int value = (int) animator.getAnimatedValue(); + if (value != tintController.getAppliedCct()) { + dtm.setColorMatrix(tintController.getLevel(), + tintController.computeMatrixForCct(value)); + tintController.setAppliedCct(value); + } + } + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + + private boolean mIsCancelled; + + @Override + public void onAnimationCancel(Animator animator) { + Slog.d(TAG, tintController.getClass().getSimpleName() + + " animation cancelled"); + mIsCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animator) { + synchronized (mCctTintApplierLock) { + Slog.d(TAG, tintController.getClass().getSimpleName() + + " animation ended: wasCancelled=" + mIsCancelled + + " toCct=" + to + + " fromCct=" + from); + if (!mIsCancelled) { + // Ensure final color matrix is set at the end of the animation. + // If the animation is cancelled then don't set the final color + // matrix so the new animator can pick up from where this one left + // off. + dtm.setColorMatrix(tintController.getLevel(), + tintController.computeMatrixForCct(to)); + tintController.setAppliedCct(to); + } + tintController.setAnimator(null); + } + } + }); + valueAnimator.start(); + } + } + } + /** * Returns the first date time corresponding to the local time that occurs before the provided * date time. @@ -747,7 +822,7 @@ public final class ColorDisplayService extends SystemService { // If disabled, clear the tint. If enabled, do nothing more here and let the next // temperature update set the correct tint. - if (!activated) { + if (oldActivated && !activated) { mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE); } } @@ -1452,7 +1527,7 @@ public final class ColorDisplayService extends SystemService { public class ColorDisplayServiceInternal { /** Sets whether DWB should be allowed in the current state. */ - public void setDisplayWhiteBalanceAllowed(boolean allowed) { + public void setDisplayWhiteBalanceAllowed(boolean allowed) { mDisplayWhiteBalanceTintController.setAllowed(allowed); updateDisplayWhiteBalanceStatus(); } @@ -1464,8 +1539,8 @@ public final class ColorDisplayService extends SystemService { * @param cct the color temperature in Kelvin. */ public boolean setDisplayWhiteBalanceColorTemperature(int cct) { - // Update the transform matrix even if it can't be applied. - mDisplayWhiteBalanceTintController.setMatrix(cct); + // Update the transform target CCT even if it can't be applied. + mDisplayWhiteBalanceTintController.setTargetCct(cct); if (mDisplayWhiteBalanceTintController.isActivated()) { mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE); @@ -1601,7 +1676,7 @@ public final class ColorDisplayService extends SystemService { applyTint(mNightDisplayTintController, false); break; case MSG_APPLY_DISPLAY_WHITE_BALANCE: - applyTint(mDisplayWhiteBalanceTintController, false); + applyTintByCct(mDisplayWhiteBalanceTintController, false); break; } } diff --git a/services/core/java/com/android/server/display/color/ColorTemperatureTintController.java b/services/core/java/com/android/server/display/color/ColorTemperatureTintController.java new file mode 100644 index 000000000000..0fbd9d409986 --- /dev/null +++ b/services/core/java/com/android/server/display/color/ColorTemperatureTintController.java @@ -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.server.display.color; + +abstract class ColorTemperatureTintController extends TintController { + + abstract int getAppliedCct(); + + abstract void setAppliedCct(int cct); + + abstract int getTargetCct(); + + abstract void setTargetCct(int cct); + + /** + * Returns the CCT value most closely associated with the "disabled" (identity) matrix for + * this device, to use as the target when deactivating this transform. + */ + abstract int getDisabledCct(); + + abstract float[] computeMatrixForCct(int cct); + + abstract CctEvaluator getEvaluator(); +} diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java index 106ac0c0d478..bf0139f347f8 100644 --- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java +++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java @@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.Size; import android.content.Context; import android.content.res.Resources; @@ -36,7 +37,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; -final class DisplayWhiteBalanceTintController extends TintController { +final class DisplayWhiteBalanceTintController extends ColorTemperatureTintController { // Three chromaticity coordinates per color: X, Y, and Z private static final int NUM_VALUES_PER_PRIMARY = 3; @@ -52,9 +53,11 @@ final class DisplayWhiteBalanceTintController extends TintController { private int mTemperatureDefault; @VisibleForTesting float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + private int mDisplayNominalWhiteCct; @VisibleForTesting ColorSpace.Rgb mDisplayColorSpaceRGB; private float[] mChromaticAdaptationMatrix; + // The temperature currently represented in the matrix. @VisibleForTesting int mCurrentColorTemperature; private float[] mCurrentColorTemperatureXYZ; @@ -65,6 +68,9 @@ final class DisplayWhiteBalanceTintController extends TintController { private Boolean mIsAvailable; // This feature becomes disallowed if the device is in an unsupported strong/light state. private boolean mIsAllowed = true; + private int mTargetCct; + private int mAppliedCct; + private CctEvaluator mCctEvaluator; private final DisplayManagerInternal mDisplayManagerInternal; @@ -108,6 +114,9 @@ final class DisplayWhiteBalanceTintController extends TintController { displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]); } + final int displayNominalWhiteCct = res.getInteger( + R.integer.config_displayWhiteBalanceDisplayNominalWhiteCct); + final int colorTemperatureMin = res.getInteger( R.integer.config_displayWhiteBalanceColorTemperatureMin); if (colorTemperatureMin <= 0) { @@ -124,19 +133,28 @@ final class DisplayWhiteBalanceTintController extends TintController { return; } - final int colorTemperature = res.getInteger( + final int defaultTemperature = res.getInteger( R.integer.config_displayWhiteBalanceColorTemperatureDefault); mTransitionDuration = res.getInteger( R.integer.config_displayWhiteBalanceTransitionTime); + int[] cctRangeMinimums = res.getIntArray( + R.array.config_displayWhiteBalanceDisplayRangeMinimums); + int[] steps = res.getIntArray(R.array.config_displayWhiteBalanceDisplaySteps); + synchronized (mLock) { mDisplayColorSpaceRGB = displayColorSpaceRGB; mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ; + mDisplayNominalWhiteCct = displayNominalWhiteCct; + mTargetCct = mDisplayNominalWhiteCct; + mAppliedCct = mDisplayNominalWhiteCct; mTemperatureMin = colorTemperatureMin; mTemperatureMax = colorTemperatureMax; - mTemperatureDefault = colorTemperature; + mTemperatureDefault = defaultTemperature; mSetUp = true; + mCctEvaluator = new CctEvaluator(mTemperatureMin, mTemperatureMax, + cctRangeMinimums, steps); } setMatrix(mTemperatureDefault); @@ -144,8 +162,16 @@ final class DisplayWhiteBalanceTintController extends TintController { @Override public float[] getMatrix() { - return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance - : ColorDisplayService.MATRIX_IDENTITY; + if (!mSetUp || !isActivated()) { + return ColorDisplayService.MATRIX_IDENTITY; + } + computeMatrixForCct(mAppliedCct); + return mMatrixDisplayWhiteBalance; + } + + @Override + public int getTargetCct() { + return mTargetCct; } /** @@ -174,6 +200,12 @@ final class DisplayWhiteBalanceTintController extends TintController { @Override public void setMatrix(int cct) { + setTargetCct(cct); + computeMatrixForCct(mTargetCct); + } + + @Override + public void setTargetCct(int cct) { if (!mSetUp) { Slog.w(ColorDisplayService.TAG, "Can't set display white balance temperature: uninitialized"); @@ -183,50 +215,93 @@ final class DisplayWhiteBalanceTintController extends TintController { if (cct < mTemperatureMin) { Slog.w(ColorDisplayService.TAG, "Requested display color temperature is below allowed minimum"); - cct = mTemperatureMin; + mTargetCct = mTemperatureMin; } else if (cct > mTemperatureMax) { Slog.w(ColorDisplayService.TAG, "Requested display color temperature is above allowed maximum"); - cct = mTemperatureMax; + mTargetCct = mTemperatureMax; + } else { + mTargetCct = cct; + } + } + + @Override + public int getDisabledCct() { + return mDisplayNominalWhiteCct; + } + + @Override + public float[] computeMatrixForCct(int cct) { + if (!mSetUp || cct == 0) { + Slog.w(ColorDisplayService.TAG, "Couldn't compute matrix for cct=" + cct); + return ColorDisplayService.MATRIX_IDENTITY; } synchronized (mLock) { mCurrentColorTemperature = cct; - // Adapt the display's nominal white point to match the requested CCT value - mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct); - - mChromaticAdaptationMatrix = - ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD, - mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); - - // Convert the adaptation matrix to RGB space - float[] result = mul3x3(mChromaticAdaptationMatrix, - mDisplayColorSpaceRGB.getTransform()); - result = mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); - - // Normalize the transform matrix to peak white value in RGB space - final float adaptedMaxR = result[0] + result[3] + result[6]; - final float adaptedMaxG = result[1] + result[4] + result[7]; - final float adaptedMaxB = result[2] + result[5] + result[8]; - final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); - - Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); - for (int i = 0; i < result.length; i++) { - result[i] /= denum; - if (!isColorMatrixCoeffValid(result[i])) { - Slog.e(ColorDisplayService.TAG, "Invalid DWB color matrix"); - return; - } + if (cct == mDisplayNominalWhiteCct && !isActivated()) { + // DWB is finished turning off. Clear the matrix. + Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); + } else { + computeMatrixForCctLocked(cct); } - java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); - java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); - java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); + Slog.d(ColorDisplayService.TAG, "computeDisplayWhiteBalanceMatrix: cct =" + cct + + " matrix =" + matrixToString(mMatrixDisplayWhiteBalance, 16)); + + return mMatrixDisplayWhiteBalance; } + } + + private void computeMatrixForCctLocked(int cct) { + // Adapt the display's nominal white point to match the requested CCT value + mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct); + + mChromaticAdaptationMatrix = + ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD, + mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); - Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct - + " matrix = " + matrixToString(mMatrixDisplayWhiteBalance, 16)); + // Convert the adaptation matrix to RGB space + float[] result = mul3x3(mChromaticAdaptationMatrix, + mDisplayColorSpaceRGB.getTransform()); + result = mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); + + // Normalize the transform matrix to peak white value in RGB space + final float adaptedMaxR = result[0] + result[3] + result[6]; + final float adaptedMaxG = result[1] + result[4] + result[7]; + final float adaptedMaxB = result[2] + result[5] + result[8]; + final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); + + Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); + + for (int i = 0; i < result.length; i++) { + result[i] /= denum; + if (!isColorMatrixCoeffValid(result[i])) { + Slog.e(ColorDisplayService.TAG, "Invalid DWB color matrix"); + return; + } + } + + java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); + java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); + java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); + } + + @Override + int getAppliedCct() { + return mAppliedCct; + } + + @Override + void setAppliedCct(int cct) { + mAppliedCct = cct; + } + + @Override + @Nullable + CctEvaluator getEvaluator() { + return mCctEvaluator; } @Override @@ -258,7 +333,10 @@ final class DisplayWhiteBalanceTintController extends TintController { pw.println(" mTemperatureMin = " + mTemperatureMin); pw.println(" mTemperatureMax = " + mTemperatureMax); pw.println(" mTemperatureDefault = " + mTemperatureDefault); + pw.println(" mDisplayNominalWhiteCct = " + mDisplayNominalWhiteCct); pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature); + pw.println(" mTargetCct = " + mTargetCct); + pw.println(" mAppliedCct = " + mAppliedCct); pw.println(" mCurrentColorTemperatureXYZ = " + matrixToString(mCurrentColorTemperatureXYZ, 3)); pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " @@ -340,11 +418,7 @@ final class DisplayWhiteBalanceTintController extends TintController { } private boolean isColorMatrixCoeffValid(float coeff) { - if (Float.isNaN(coeff) || Float.isInfinite(coeff)) { - return false; - } - - return true; + return !Float.isNaN(coeff) && !Float.isInfinite(coeff); } private boolean isColorMatrixValid(float[] matrix) { @@ -352,8 +426,8 @@ final class DisplayWhiteBalanceTintController extends TintController { return false; } - for (int i = 0; i < matrix.length; i++) { - if (!isColorMatrixCoeffValid(matrix[i])) { + for (float value : matrix) { + if (!isColorMatrixCoeffValid(value)) { return false; } } diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java index c53ac06c66e1..384333a574b5 100644 --- a/services/core/java/com/android/server/display/color/TintController.java +++ b/services/core/java/com/android/server/display/color/TintController.java @@ -16,6 +16,7 @@ package com.android.server.display.color; +import android.animation.ValueAnimator; import android.content.Context; import android.util.Slog; @@ -28,14 +29,14 @@ abstract class TintController { */ private static final long TRANSITION_DURATION = 3000L; - private ColorDisplayService.TintValueAnimator mAnimator; + private ValueAnimator mAnimator; private Boolean mIsActivated; - public ColorDisplayService.TintValueAnimator getAnimator() { + public ValueAnimator getAnimator() { return mAnimator; } - public void setAnimator(ColorDisplayService.TintValueAnimator animator) { + public void setAnimator(ValueAnimator animator) { mAnimator = animator; } diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index d8716b328230..6ec4022fe15a 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -562,7 +562,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { key.append(",languageTag:").append(inputDevice.getKeyboardLanguageTag()); } if (!TextUtils.isEmpty(inputDevice.getKeyboardLayoutType())) { - key.append(",layoutType:").append(inputDevice.getKeyboardLanguageTag()); + key.append(",layoutType:").append(inputDevice.getKeyboardLayoutType()); } } return key.toString(); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 464a256bd30e..02b705344cb9 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -16,6 +16,12 @@ package com.android.server.media; +import static android.media.VolumeProvider.VOLUME_CONTROL_ABSOLUTE; +import static android.media.VolumeProvider.VOLUME_CONTROL_FIXED; +import static android.media.VolumeProvider.VOLUME_CONTROL_RELATIVE; +import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL; +import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -173,8 +179,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR // Volume handling fields private AudioAttributes mAudioAttrs; private AudioManager mAudioManager; - private int mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; - private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; + private int mVolumeType = PLAYBACK_TYPE_LOCAL; + private int mVolumeControlType = VOLUME_CONTROL_ABSOLUTE; private int mMaxVolume = 0; private int mCurrentVolume = 0; private int mOptimisticVolume = -1; @@ -309,13 +315,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (checkPlaybackActiveState(true) || isSystemPriority()) { flags &= ~AudioManager.FLAG_PLAY_SOUND; } - if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { + if (mVolumeType == PLAYBACK_TYPE_LOCAL) { // Adjust the volume with a handler not to be blocked by other system service. int stream = getVolumeStream(mAudioAttrs); postAdjustLocalVolume(stream, direction, flags, opPackageName, pid, uid, asSystemService, useSuggested, previousFlagPlaySound); } else { - if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) { + if (mVolumeControlType == VOLUME_CONTROL_FIXED) { if (DEBUG) { Log.d(TAG, "Session does not support volume adjustment"); } @@ -354,7 +360,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR private void setVolumeTo(String packageName, String opPackageName, int pid, int uid, int value, int flags) { - if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { + if (mVolumeType == PLAYBACK_TYPE_LOCAL) { int stream = getVolumeStream(mAudioAttrs); final int volumeValue = value; mHandler.post(new Runnable() { @@ -371,7 +377,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } }); } else { - if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) { + if (mVolumeControlType != VOLUME_CONTROL_ABSOLUTE) { if (DEBUG) { Log.d(TAG, "Session does not support setting volume"); } @@ -433,7 +439,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR */ @Override public boolean isPlaybackTypeLocal() { - return mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; + return mVolumeType == PLAYBACK_TYPE_LOCAL; } @Override @@ -495,7 +501,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR @Override public boolean canHandleVolumeKey() { - return mVolumeControlType != VolumeProvider.VOLUME_CONTROL_FIXED; + return mVolumeControlType != VOLUME_CONTROL_FIXED; } @Override @@ -528,13 +534,48 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR pw.println(indent + "controllers: " + mControllerCallbackHolders.size()); pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString())); pw.println(indent + "audioAttrs=" + mAudioAttrs); - pw.println(indent + "volumeType=" + mVolumeType + ", controlType=" + mVolumeControlType - + ", max=" + mMaxVolume + ", current=" + mCurrentVolume); + pw.append(indent) + .append("volumeType=") + .append(toVolumeTypeString(mVolumeType)) + .append(", controlType=") + .append(toVolumeControlTypeString(mVolumeControlType)) + .append(", max=") + .append(Integer.toString(mMaxVolume)) + .append(", current=") + .append(Integer.toString(mCurrentVolume)) + .append(", volumeControlId=") + .append(mVolumeControlId) + .println(); pw.println(indent + "metadata: " + mMetadataDescription); pw.println(indent + "queueTitle=" + mQueueTitle + ", size=" + (mQueue == null ? 0 : mQueue.size())); } + private static String toVolumeControlTypeString( + @VolumeProvider.ControlType int volumeControlType) { + switch (volumeControlType) { + case VOLUME_CONTROL_FIXED: + return "FIXED"; + case VOLUME_CONTROL_RELATIVE: + return "RELATIVE"; + case VOLUME_CONTROL_ABSOLUTE: + return "ABSOLUTE"; + default: + return TextUtils.formatSimple("unknown(%d)", volumeControlType); + } + } + + private static String toVolumeTypeString(@PlaybackInfo.PlaybackType int volumeType) { + switch (volumeType) { + case PLAYBACK_TYPE_LOCAL: + return "LOCAL"; + case PLAYBACK_TYPE_REMOTE: + return "REMOTE"; + default: + return TextUtils.formatSimple("unknown(%d)", volumeType); + } + } + @Override public String toString() { return mPackageName + "/" + mTag + " (userId=" + mUserId + ")"; @@ -877,8 +918,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR int stream = getVolumeStream(attributes); int max = mAudioManager.getStreamMaxVolume(stream); int current = mAudioManager.getStreamVolume(stream); - return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, - current, attributes, null); + return new PlaybackInfo( + volumeType, VOLUME_CONTROL_ABSOLUTE, max, current, attributes, null); } private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { @@ -1124,7 +1165,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR boolean typeChanged; synchronized (mLock) { typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE; - mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; + mVolumeType = PLAYBACK_TYPE_LOCAL; mVolumeControlId = null; if (attributes != null) { mAudioAttrs = attributes; @@ -1148,7 +1189,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR throws RemoteException { boolean typeChanged; synchronized (mLock) { - typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; + typeChanged = mVolumeType == PLAYBACK_TYPE_LOCAL; mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE; mVolumeControlType = control; mMaxVolume = max; diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index a267e8afa78d..5932929a69c4 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -4360,12 +4360,22 @@ final class InstallPackageHelper { // A new application appeared on /system, and we are seeing it for the first time. // Its also not updated as we don't have a copy of it on /data. So, scan it in a - // STOPPED state. Ignore if it's an APEX package since stopped state does not affect them. + // STOPPED state. + // We'll skip this step under the following conditions: + // - It's "android" + // - It's an APEX or overlay package since stopped state does not affect them. + // - It is enumerated with a <initial-package-state> tag having the stopped attribute + // set to false final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0; - if (mPm.mShouldStopSystemPackagesByDefault && scanSystemPartition - && !pkgAlreadyExists && !isApexPkg) { + if (mPm.mShouldStopSystemPackagesByDefault + && scanSystemPartition + && !pkgAlreadyExists + && !isApexPkg + && !parsedPackage.isOverlayIsStatic() + ) { String packageName = parsedPackage.getPackageName(); - if (!mPm.mInitialNonStoppedSystemPackages.contains(packageName)) { + if (!mPm.mInitialNonStoppedSystemPackages.contains(packageName) + && !"android".contentEquals(packageName)) { scanFlags |= SCAN_AS_STOPPED_SYSTEM_APP; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b5108af6d2f8..f48204645d34 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5258,6 +5258,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + public @NonNull List<String> getInitialNonStoppedSystemPackages() { + return mInitialNonStoppedSystemPackages != null + ? new ArrayList<>(mInitialNonStoppedSystemPackages) : new ArrayList<>(); + } + + @Override public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) { Objects.requireNonNull(packageNames, "packageNames cannot be null"); mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index f78b611a70e3..58183f0bc16a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -782,6 +782,8 @@ class PackageManagerShellCommand extends ShellCommand { getInFileDescriptor(), getOutFileDescriptor(), getErrFileDescriptor(), new String[] { "list" }, getShellCallback(), adoptResultReceiver()); return 0; + case "initial-non-stopped-system-packages": + return runListInitialNonStoppedSystemPackages(); } pw.println("Error: unknown list type '" + type + "'"); return -1; @@ -794,6 +796,21 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runListInitialNonStoppedSystemPackages() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final List<String> list = mInterface.getInitialNonStoppedSystemPackages(); + + Collections.sort(list); + + for (String pkgName : list) { + pw.print("package:"); + pw.print(pkgName); + pw.println(); + } + + return 0; + } + private int runListFeatures() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final List<FeatureInfo> list = mInterface.getSystemAvailableFeatures().getList(); diff --git a/services/core/java/com/android/server/pm/UserJourneyLogger.java b/services/core/java/com/android/server/pm/UserJourneyLogger.java new file mode 100644 index 000000000000..f48a1669c259 --- /dev/null +++ b/services/core/java/com/android/server/pm/UserJourneyLogger.java @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.os.UserManager.USER_TYPE_FULL_DEMO; +import static android.os.UserManager.USER_TYPE_FULL_GUEST; +import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED; +import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; +import static android.os.UserManager.USER_TYPE_FULL_SYSTEM; +import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; +import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; +import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS; + +import static com.android.internal.util.FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.UserInfo; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * This class is logging User Lifecycle statsd events and synchronise User Lifecycle Journeys + * by making sure all events are called in correct order and errors are reported in case of + * unexpected journeys. This class also makes sure that all user sub-journeys are logged so + * for example User Switch also log User Start Journey. + */ +public class UserJourneyLogger { + + public static final int ERROR_CODE_INVALID_SESSION_ID = 0; + public static final int ERROR_CODE_UNSPECIFIED = -1; + /* + * Possible reasons for ERROR_CODE_INCOMPLETE_OR_TIMEOUT to occur: + * - A user switch journey is received while another user switch journey is in + * process for the same user. + * - A user switch journey is received while user start journey is in process for + * the same user. + * - A user start journey is received while another user start journey is in process + * for the same user. + * In all cases potentially an incomplete, timed-out session or multiple + * simultaneous requests. It is not possible to keep track of multiple sessions for + * the same user, so previous session is abandoned. + */ + public static final int ERROR_CODE_INCOMPLETE_OR_TIMEOUT = 2; + public static final int ERROR_CODE_ABORTED = 3; + public static final int ERROR_CODE_NULL_USER_INFO = 4; + public static final int ERROR_CODE_USER_ALREADY_AN_ADMIN = 5; + public static final int ERROR_CODE_USER_IS_NOT_AN_ADMIN = 6; + + @IntDef(prefix = {"ERROR_CODE"}, value = { + ERROR_CODE_UNSPECIFIED, + ERROR_CODE_INCOMPLETE_OR_TIMEOUT, + ERROR_CODE_ABORTED, + ERROR_CODE_NULL_USER_INFO, + ERROR_CODE_USER_ALREADY_AN_ADMIN, + ERROR_CODE_USER_IS_NOT_AN_ADMIN, + ERROR_CODE_INVALID_SESSION_ID + }) + public @interface UserJourneyErrorCode { + } + + // The various user journeys, defined in the UserLifecycleJourneyReported atom for statsd + public static final int USER_JOURNEY_UNKNOWN = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__UNKNOWN; + public static final int USER_JOURNEY_USER_SWITCH_FG = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_FG; + public static final int USER_JOURNEY_USER_SWITCH_UI = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_SWITCH_UI; + public static final int USER_JOURNEY_USER_START = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START; + public static final int USER_JOURNEY_USER_CREATE = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE; + public static final int USER_JOURNEY_USER_STOP = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP; + public static final int USER_JOURNEY_USER_REMOVE = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE; + public static final int USER_JOURNEY_GRANT_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN; + public static final int USER_JOURNEY_REVOKE_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN; + + @IntDef(prefix = {"USER_JOURNEY"}, value = { + USER_JOURNEY_UNKNOWN, + USER_JOURNEY_USER_SWITCH_FG, + USER_JOURNEY_USER_SWITCH_UI, + USER_JOURNEY_USER_START, + USER_JOURNEY_USER_STOP, + USER_JOURNEY_USER_CREATE, + USER_JOURNEY_USER_REMOVE, + USER_JOURNEY_GRANT_ADMIN, + USER_JOURNEY_REVOKE_ADMIN + }) + public @interface UserJourney { + } + + + // The various user lifecycle events, defined in the UserLifecycleEventOccurred atom for statsd + public static final int USER_LIFECYCLE_EVENT_UNKNOWN = + USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN; + public static final int USER_LIFECYCLE_EVENT_SWITCH_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__SWITCH_USER; + public static final int USER_LIFECYCLE_EVENT_START_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__START_USER; + public static final int USER_LIFECYCLE_EVENT_CREATE_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; + public static final int USER_LIFECYCLE_EVENT_REMOVE_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; + public static final int USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__USER_RUNNING_LOCKED; + public static final int USER_LIFECYCLE_EVENT_UNLOCKING_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER; + public static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER; + public static final int USER_LIFECYCLE_EVENT_STOP_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER; + public static final int USER_LIFECYCLE_EVENT_GRANT_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; + public static final int USER_LIFECYCLE_EVENT_REVOKE_ADMIN = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; + + @IntDef(prefix = {"USER_LIFECYCLE_EVENT"}, value = { + USER_LIFECYCLE_EVENT_UNKNOWN, + USER_LIFECYCLE_EVENT_SWITCH_USER, + USER_LIFECYCLE_EVENT_START_USER, + USER_LIFECYCLE_EVENT_CREATE_USER, + USER_LIFECYCLE_EVENT_REMOVE_USER, + USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + USER_LIFECYCLE_EVENT_UNLOCKING_USER, + USER_LIFECYCLE_EVENT_UNLOCKED_USER, + USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_GRANT_ADMIN, + USER_LIFECYCLE_EVENT_REVOKE_ADMIN + }) + public @interface UserLifecycleEvent { + } + + // User lifecycle event state, defined in the UserLifecycleEventOccurred atom for statsd + public static final int EVENT_STATE_BEGIN = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN; + public static final int EVENT_STATE_FINISH = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH; + public static final int EVENT_STATE_NONE = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE; + public static final int EVENT_STATE_CANCEL = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__CANCEL; + public static final int EVENT_STATE_ERROR = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR; + + @IntDef(prefix = {"EVENT_STATE"}, value = { + EVENT_STATE_BEGIN, + EVENT_STATE_FINISH, + EVENT_STATE_NONE, + EVENT_STATE_CANCEL, + EVENT_STATE_ERROR, + }) + public @interface UserLifecycleEventState { + } + + private static final int USER_ID_KEY_MULTIPLICATION = 100; + + private final Object mLock = new Object(); + + /** + * {@link UserIdInt} and {@link UserJourney} to {@link UserJourneySession} mapping used for + * statsd logging for the UserLifecycleJourneyReported and UserLifecycleEventOccurred atoms. + */ + @GuardedBy("mLock") + private final SparseArray<UserJourneySession> mUserIdToUserJourneyMap = new SparseArray<>(); + + /** + * Returns event equivalent of given journey + */ + @UserLifecycleEvent + private static int journeyToEvent(@UserJourney int journey) { + switch (journey) { + case USER_JOURNEY_USER_SWITCH_UI: + case USER_JOURNEY_USER_SWITCH_FG: + return USER_LIFECYCLE_EVENT_SWITCH_USER; + case USER_JOURNEY_USER_START: + return USER_LIFECYCLE_EVENT_START_USER; + case USER_JOURNEY_USER_CREATE: + return USER_LIFECYCLE_EVENT_CREATE_USER; + case USER_JOURNEY_USER_STOP: + return USER_LIFECYCLE_EVENT_STOP_USER; + case USER_JOURNEY_USER_REMOVE: + return USER_LIFECYCLE_EVENT_REMOVE_USER; + case USER_JOURNEY_GRANT_ADMIN: + return USER_LIFECYCLE_EVENT_GRANT_ADMIN; + case USER_JOURNEY_REVOKE_ADMIN: + return USER_LIFECYCLE_EVENT_REVOKE_ADMIN; + default: + return USER_LIFECYCLE_EVENT_UNKNOWN; + } + } + + /** + * Returns the enum defined in the statsd UserLifecycleJourneyReported atom corresponding to + * the user type. + * Changes to this method require changes in CTS file + * com.android.cts.packagemanager.stats.device.UserInfoUtil + * which is duplicate for CTS tests purposes. + */ + public static int getUserTypeForStatsd(@NonNull String userType) { + switch (userType) { + case USER_TYPE_FULL_SYSTEM: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SYSTEM; + case USER_TYPE_FULL_SECONDARY: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY; + case USER_TYPE_FULL_GUEST: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_GUEST; + case USER_TYPE_FULL_DEMO: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_DEMO; + case USER_TYPE_FULL_RESTRICTED: + return FrameworkStatsLog + .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_RESTRICTED; + case USER_TYPE_PROFILE_MANAGED: + return FrameworkStatsLog + .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_MANAGED; + case USER_TYPE_SYSTEM_HEADLESS: + return FrameworkStatsLog + .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS; + case USER_TYPE_PROFILE_CLONE: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE; + default: + return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN; + } + } + + /** + * Map error code to the event finish state. + */ + @UserLifecycleEventState + private static int errorToFinishState(@UserJourneyErrorCode int errorCode) { + switch (errorCode) { + case ERROR_CODE_ABORTED: + return EVENT_STATE_CANCEL; + case ERROR_CODE_UNSPECIFIED: + return EVENT_STATE_FINISH; + default: + return EVENT_STATE_ERROR; + } + } + + /** + * Simply logging USER_LIFECYCLE_JOURNEY_REPORTED if session exists. + * If session does not exist then it logs ERROR_CODE_INVALID_SESSION_ID + */ + @VisibleForTesting + public void logUserLifecycleJourneyReported(@Nullable UserJourneySession session, + @UserJourney int journey, @UserIdInt int originalUserId, @UserIdInt int targetUserId, + int userType, int userFlags, @UserJourneyErrorCode int errorCode) { + if (session == null) { + writeUserLifecycleJourneyReported(-1, journey, originalUserId, targetUserId, + userType, userFlags, ERROR_CODE_INVALID_SESSION_ID); + } else { + writeUserLifecycleJourneyReported( + session.mSessionId, journey, originalUserId, targetUserId, userType, userFlags, + errorCode); + } + } + + /** + * Helper method for spy testing + */ + @VisibleForTesting + public void writeUserLifecycleJourneyReported(long sessionId, int journey, int originalUserId, + int targetUserId, int userType, int userFlags, int errorCode) { + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, + sessionId, journey, originalUserId, targetUserId, userType, userFlags, + errorCode); + } + + /** + * Simply logging USER_LIFECYCLE_EVENT_OCCURRED if session exists. + * If session does not exist then it logs ERROR_CODE_INVALID_SESSION_ID + * and EVENT_STATE_ERROR + */ + @VisibleForTesting + public void logUserLifecycleEventOccurred(UserJourneySession session, + @UserIdInt int targetUserId, @UserLifecycleEvent int event, + @UserLifecycleEventState int state, @UserJourneyErrorCode int errorCode) { + if (session == null) { + writeUserLifecycleEventOccurred(-1, targetUserId, event, + EVENT_STATE_ERROR, ERROR_CODE_INVALID_SESSION_ID); + } else { + writeUserLifecycleEventOccurred(session.mSessionId, targetUserId, event, state, + errorCode); + } + } + + /** + * Helper method for spy testing + */ + @VisibleForTesting + public void writeUserLifecycleEventOccurred(long sessionId, int userId, int event, int state, + int errorCode) { + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, + sessionId, userId, event, state, errorCode); + } + + /** + * statsd helper method for logging the given event for the UserLifecycleEventOccurred statsd + * atom. It finds the user journey session for target user id and logs it as that journey. + */ + public void logUserLifecycleEvent(@UserIdInt int userId, @UserLifecycleEvent int event, + @UserLifecycleEventState int eventState) { + final UserJourneySession userJourneySession = findUserJourneySession(userId); + logUserLifecycleEventOccurred(userJourneySession, userId, + event, eventState, UserJourneyLogger.ERROR_CODE_UNSPECIFIED); + } + + /** + * Returns first user session from mUserIdToUserJourneyMap for given user id, + * or null if user id was not found in mUserIdToUserJourneyMap. + */ + private @Nullable UserJourneySession findUserJourneySession(@UserIdInt int userId) { + synchronized (mLock) { + final int keyMapSize = mUserIdToUserJourneyMap.size(); + for (int i = 0; i < keyMapSize; i++) { + int key = mUserIdToUserJourneyMap.keyAt(i); + if (key / USER_ID_KEY_MULTIPLICATION == userId) { + return mUserIdToUserJourneyMap.get(key); + } + } + } + return null; + } + + /** + * Returns unique id for user and journey. For example if user id = 11 and journey = 7 + * then unique key = 11 * 100 + 7 = 1107 + */ + private int getUserJourneyKey(@UserIdInt int targetUserId, @UserJourney int journey) { + // We leave 99 for user journeys ids. + return (targetUserId * USER_ID_KEY_MULTIPLICATION) + journey; + } + + /** + * Special use case when user journey incomplete or timeout and current user is unclear + */ + @VisibleForTesting + public UserJourneySession finishAndClearIncompleteUserJourney(@UserIdInt int targetUserId, + @UserJourney int journey) { + synchronized (mLock) { + final int key = getUserJourneyKey(targetUserId, journey); + final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key); + if (userJourneySession != null) { + logUserLifecycleEventOccurred( + userJourneySession, + targetUserId, + journeyToEvent(userJourneySession.mJourney), + EVENT_STATE_ERROR, + UserJourneyLogger.ERROR_CODE_INCOMPLETE_OR_TIMEOUT); + + logUserLifecycleJourneyReported( + userJourneySession, + journey, + /* originalUserId= */ -1, + targetUserId, + getUserTypeForStatsd(""), -1, + ERROR_CODE_INCOMPLETE_OR_TIMEOUT); + mUserIdToUserJourneyMap.remove(key); + + return userJourneySession; + } + } + return null; + } + + /** + * Log user journey event and report finishing without error + */ + public UserJourneySession logUserJourneyFinish(@UserIdInt int originalUserId, + UserInfo targetUser, @UserJourney int journey) { + return logUserJourneyFinishWithError(originalUserId, targetUser, journey, + ERROR_CODE_UNSPECIFIED); + } + + /** + * Special case when it is unknown which user switch journey was used and checking both + */ + @VisibleForTesting + public UserJourneySession logUserSwitchJourneyFinish(@UserIdInt int originalUserId, + UserInfo targetUser) { + synchronized (mLock) { + final int key_fg = getUserJourneyKey(targetUser.id, USER_JOURNEY_USER_SWITCH_FG); + final int key_ui = getUserJourneyKey(targetUser.id, USER_JOURNEY_USER_SWITCH_UI); + + if (mUserIdToUserJourneyMap.contains(key_fg)) { + return logUserJourneyFinish(originalUserId, targetUser, + USER_JOURNEY_USER_SWITCH_FG); + } + + if (mUserIdToUserJourneyMap.contains(key_ui)) { + return logUserJourneyFinish(originalUserId, targetUser, + USER_JOURNEY_USER_SWITCH_UI); + } + + return null; + } + } + + /** + * Log user journey event and report finishing with error + */ + public UserJourneySession logUserJourneyFinishWithError(@UserIdInt int originalUserId, + UserInfo targetUser, @UserJourney int journey, @UserJourneyErrorCode int errorCode) { + synchronized (mLock) { + final int state = errorToFinishState(errorCode); + final int key = getUserJourneyKey(targetUser.id, journey); + final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key); + if (userJourneySession != null) { + logUserLifecycleEventOccurred( + userJourneySession, targetUser.id, + journeyToEvent(userJourneySession.mJourney), + state, + errorCode); + + logUserLifecycleJourneyReported( + userJourneySession, + journey, originalUserId, targetUser.id, + getUserTypeForStatsd(targetUser.userType), + targetUser.flags, + errorCode); + mUserIdToUserJourneyMap.remove(key); + + return userJourneySession; + } + } + return null; + } + + /** + * Log event and report finish when user is null. This is edge case when UserInfo + * can not be passed because it is null, therefore all information are passed as arguments. + */ + public UserJourneySession logNullUserJourneyError(@UserJourney int journey, + @UserIdInt int currentUserId, @UserIdInt int targetUserId, String targetUserType, + int targetUserFlags) { + synchronized (mLock) { + final int key = getUserJourneyKey(targetUserId, journey); + final UserJourneySession session = mUserIdToUserJourneyMap.get(key); + + logUserLifecycleEventOccurred( + session, targetUserId, journeyToEvent(journey), + EVENT_STATE_ERROR, + ERROR_CODE_NULL_USER_INFO); + + logUserLifecycleJourneyReported( + session, journey, currentUserId, targetUserId, + getUserTypeForStatsd(targetUserType), targetUserFlags, + ERROR_CODE_NULL_USER_INFO); + + mUserIdToUserJourneyMap.remove(key); + return session; + } + } + + /** + * Log for user creation finish event and report. This is edge case when target user id is + * different in begin event and finish event as it is unknown what is user id + * until it has been created. + */ + public UserJourneySession logUserCreateJourneyFinish(@UserIdInt int originalUserId, + UserInfo targetUser) { + synchronized (mLock) { + // we do not know user id until we create new user which is why we use -1 + // as user id to create and find session, but we log correct id. + final int key = getUserJourneyKey(-1, USER_JOURNEY_USER_CREATE); + final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(key); + if (userJourneySession != null) { + logUserLifecycleEventOccurred( + userJourneySession, targetUser.id, + USER_LIFECYCLE_EVENT_CREATE_USER, + EVENT_STATE_FINISH, + ERROR_CODE_UNSPECIFIED); + + logUserLifecycleJourneyReported( + userJourneySession, + USER_JOURNEY_USER_CREATE, originalUserId, targetUser.id, + getUserTypeForStatsd(targetUser.userType), + targetUser.flags, + ERROR_CODE_UNSPECIFIED); + mUserIdToUserJourneyMap.remove(key); + + return userJourneySession; + } + } + return null; + } + + /** + * Adds new UserJourneySession to mUserIdToUserJourneyMap and log UserJourneyEvent Begin state + */ + public UserJourneySession logUserJourneyBegin(@UserIdInt int targetId, + @UserJourney int journey) { + final long newSessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); + synchronized (mLock) { + final int key = getUserJourneyKey(targetId, journey); + final UserJourneySession userJourneySession = + new UserJourneySession(newSessionId, journey); + mUserIdToUserJourneyMap.append(key, userJourneySession); + + logUserLifecycleEventOccurred( + userJourneySession, targetId, + journeyToEvent(userJourneySession.mJourney), + EVENT_STATE_BEGIN, + ERROR_CODE_UNSPECIFIED); + + return userJourneySession; + } + } + + /** + * Helper class to store user journey and session id. + * + * <p> User journey tracks a chain of user lifecycle events occurring during different user + * activities such as user start, user switch, and user creation. + */ + public static class UserJourneySession { + public final long mSessionId; + @UserJourney + public final int mJourney; + + @VisibleForTesting + public UserJourneySession(long sessionId, @UserJourney int journey) { + mJourney = journey; + mSessionId = sessionId; + } + } +} diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 5f8efe29459d..b92cdde5910f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -23,6 +23,15 @@ import static android.os.UserManager.DISALLOW_USER_SWITCH; import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY; import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_ALREADY_AN_ADMIN; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_IS_NOT_AN_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_CREATE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_REMOVE; + import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; @@ -162,7 +171,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -519,6 +527,8 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUserLifecycleListeners") private final ArrayList<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>(); + private final UserJourneyLogger mUserJourneyLogger = new UserJourneyLogger(); + private final LockPatternUtils mLockPatternUtils; private final String ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK = @@ -1580,45 +1590,56 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("set user admin"); - final long sessionId = logGrantAdminJourneyBegin(userId); + mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_GRANT_ADMIN); UserInfo info; synchronized (mPackagesLock) { synchronized (mUsersLock) { info = getUserInfoLU(userId); } - if (info == null || info.isAdmin()) { - // Exit if no user found with that id, or the user is already an Admin. - logUserJourneyError(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN, - userId); + if (info == null) { + // Exit if no user found with that id, + mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN, + getCurrentUserId(), userId, /* userType */ "", /* userFlags */ -1); + return; + } else if (info.isAdmin()) { + // Exit if the user is already an Admin. + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), info, + USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_USER_ALREADY_AN_ADMIN); return; } info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(getUserDataLU(info.id)); } - logGrantAdminJourneyFinish(sessionId, userId, info.userType, info.flags); + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), info, + USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_UNSPECIFIED); } @Override public void revokeUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("revoke admin privileges"); - final long sessionId = logRevokeAdminJourneyBegin(userId); + mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_REVOKE_ADMIN); UserData user; synchronized (mPackagesLock) { synchronized (mUsersLock) { user = getUserDataLU(userId); - if (user == null || !user.info.isAdmin()) { - // Exit if no user found with that id, or the user is not an Admin. - logUserJourneyError(sessionId, FrameworkStatsLog - .USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN, - userId); + if (user == null) { + // Exit if no user found with that id + mUserJourneyLogger.logNullUserJourneyError( + USER_JOURNEY_REVOKE_ADMIN, + getCurrentUserId(), userId, "", -1); + return; + } else if (!user.info.isAdmin()) { + // Exit if no user is not an Admin. + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), user.info, + USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_USER_IS_NOT_AN_ADMIN); return; } user.info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(user); } } - logRevokeAdminJourneyFinish(sessionId, userId, user.info.userType, user.info.flags); + mUserJourneyLogger.logUserJourneyFinishWithError(getCurrentUserId(), user.info, + USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_UNSPECIFIED); } /** @@ -4700,16 +4721,20 @@ public class UserManagerService extends IUserManager.Stub { final int noneUserId = -1; final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("createUser-" + flags); - final long sessionId = logUserCreateJourneyBegin(noneUserId); + mUserJourneyLogger.logUserJourneyBegin(noneUserId, USER_JOURNEY_USER_CREATE); UserInfo newUser = null; try { newUser = createUserInternalUncheckedNoTracing(name, userType, flags, parentId, preCreate, disallowedPackages, t, token); return newUser; } finally { - logUserCreateJourneyFinish(sessionId, - newUser != null ? newUser.id : noneUserId, userType, flags, - newUser != null); + if (newUser != null) { + mUserJourneyLogger.logUserCreateJourneyFinish(getCurrentUserId(), newUser); + } else { + mUserJourneyLogger.logNullUserJourneyError( + USER_JOURNEY_USER_CREATE, + getCurrentUserId(), noneUserId, userType, flags); + } t.traceEnd(); } } @@ -5198,137 +5223,6 @@ public class UserManagerService extends IUserManager.Stub { && !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED); } - private long logUserCreateJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE, - userId); - } - - private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags, boolean finish) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE, - userId, userType, flags, finish); - } - - private long logUserRemoveJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE, - userId); - } - - private void logUserRemoveJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags, boolean finish) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE, - userId, userType, flags, finish); - } - - private long logGrantAdminJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN, - userId); - } - - private void logGrantAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN, - userId, userType, flags, true); - } - - private long logRevokeAdminJourneyBegin(@UserIdInt int userId) { - return logUserJourneyBegin( - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN, - userId); - } - - private void logRevokeAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType, - @UserInfoFlag int flags) { - logUserJourneyFinish(sessionId, - FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN, - userId, userType, flags, true); - } - - private void logUserJourneyFinish(long sessionId, int journey, @UserIdInt int userId, - String userType, @UserInfoFlag int flags, boolean finish) { - - // log the journey atom with the user metadata - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, - journey, /* origin_user= */ getCurrentUserId(), userId, - UserManager.getUserTypeForStatsd(userType), flags); - - int event; - switch (journey) { - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; - break; - default: - throw new IllegalArgumentException("Journey " + journey + " not expected."); - } - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, - finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH - : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); - } - - private long logUserJourneyBegin(int journey, @UserIdInt int userId) { - final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); - - // log the event atom to indicate the event start - int event; - switch (journey) { - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; - break; - default: - throw new IllegalArgumentException("Journey " + journey + " not expected."); - } - - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); - return sessionId; - } - - private void logUserJourneyError(long sessionId, int journey, @UserIdInt int userId) { - - // log the journey atom with the user metadata - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, - journey, /* origin_user= */ getCurrentUserId(), userId); - - int event; - switch (journey) { - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN; - break; - case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN: - event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN; - break; - default: - throw new IllegalArgumentException("Journey " + journey + " not expected."); - } - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR); - } - /** Register callbacks for statsd pulled atoms. */ private void registerStatsCallbacks() { final StatsManager statsManager = mContext.getSystemService(StatsManager.class); @@ -5352,7 +5246,8 @@ public class UserManagerService extends IUserManager.Stub { if (size > 1) { for (int idx = 0; idx < size; idx++) { final UserInfo user = users.get(idx); - final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType); + final int userTypeStandard = mUserJourneyLogger + .getUserTypeForStatsd(user.userType); final String userTypeCustom = (userTypeStandard == FrameworkStatsLog .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ? @@ -5635,7 +5530,7 @@ public class UserManagerService extends IUserManager.Stub { writeUserLP(userData); } - final long sessionId = logUserRemoveJourneyBegin(userId); + mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_USER_REMOVE); try { mAppOpsService.removeUser(userId); @@ -5657,13 +5552,17 @@ public class UserManagerService extends IUserManager.Stub { @Override public void userStopped(int userIdParam) { finishRemoveUser(userIdParam); - logUserRemoveJourneyFinish(sessionId, userIdParam, - userData.info.userType, userData.info.flags, true); + int originUserId = UserManagerService.this.getCurrentUserId(); + mUserJourneyLogger.logUserJourneyFinishWithError(originUserId, + userData.info, USER_JOURNEY_USER_REMOVE, + ERROR_CODE_UNSPECIFIED); } @Override public void userStopAborted(int userIdParam) { - logUserRemoveJourneyFinish(sessionId, userIdParam, - userData.info.userType, userData.info.flags, false); + int originUserId = UserManagerService.this.getCurrentUserId(); + mUserJourneyLogger.logUserJourneyFinishWithError(originUserId, + userData.info, USER_JOURNEY_USER_REMOVE, + ERROR_CODE_ABORTED); } }); } catch (RemoteException e) { @@ -7297,9 +7196,9 @@ public class UserManagerService extends IUserManager.Stub { final UserInfo userInfo = getUserInfo(userIds[i]); if (userInfo == null) { // Not possible because the input user ids should all be valid - userTypes[i] = UserManager.getUserTypeForStatsd(""); + userTypes[i] = mUserJourneyLogger.getUserTypeForStatsd(""); } else { - userTypes[i] = UserManager.getUserTypeForStatsd(userInfo.userType); + userTypes[i] = mUserJourneyLogger.getUserTypeForStatsd(userInfo.userType); } } return userTypes; @@ -7536,4 +7435,11 @@ public class UserManagerService extends IUserManager.Stub { .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser); } + /** + * Returns instance of {@link com.android.server.pm.UserJourneyLogger}. + */ + public UserJourneyLogger getUserJourneyLogger() { + return mUserJourneyLogger; + } + } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 9ff98be6f5cd..f8954b7c7f95 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -5690,8 +5690,14 @@ public final class PowerManagerService extends SystemService } if (eventTime > now) { - Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now); - throw new IllegalArgumentException("event time must not be in the future"); + Slog.wtf(TAG, "Event cannot be newer than the current time (" + + "now=" + now + + ", eventTime=" + eventTime + + ", displayId=" + displayId + + ", event=" + PowerManager.userActivityEventToString(event) + + ", flags=" + flags + + ")"); + return; } final int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 24a271f93e29..26b40b4c09ee 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -582,6 +582,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mPauseSchedulePendingForPip = false; + // Gets set to indicate that the activity is currently being auto-pipped. + boolean mAutoEnteringPip = false; + private void updateEnterpriseThumbnailDrawable(Context context) { DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); mEnterpriseThumbnailDrawable = dpm.getResources().getDrawable( @@ -6099,8 +6102,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token, PauseActivityItem.obtain(finishing, false /* userLeaving */, - configChangeFlags, false /* dontReport */, - false /* autoEnteringPip */)); + configChangeFlags, false /* dontReport */, mAutoEnteringPip)); } catch (Exception e) { Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a0ea1c3dbdbf..b816dad2a62e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3592,15 +3592,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + boolean enterPictureInPictureMode(@NonNull ActivityRecord r, + @NonNull PictureInPictureParams params, boolean fromClient) { + return enterPictureInPictureMode(r, params, fromClient, false /* isAutoEnter */); + } + /** * Puts the given activity in picture in picture mode if possible. * * @param fromClient true if this comes from a client call (eg. Activity.enterPip). + * @param isAutoEnter true if this comes from an automatic pip-enter. * @return true if the activity is now in picture-in-picture mode, or false if it could not * enter picture-in-picture mode. */ boolean enterPictureInPictureMode(@NonNull ActivityRecord r, - @NonNull PictureInPictureParams params, boolean fromClient) { + @NonNull PictureInPictureParams params, boolean fromClient, boolean isAutoEnter) { // If the activity is already in picture in picture mode, then just return early if (r.inPinnedWindowingMode()) { return true; @@ -3635,6 +3641,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } r.setPictureInPictureParams(params); + r.mAutoEnteringPip = isAutoEnter; mRootWindowContainer.moveActivityToPinnedRootTask(r, null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", transition); @@ -3643,6 +3650,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.getTask().schedulePauseActivity(r, false /* userLeaving */, false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip"); } + r.mAutoEnteringPip = false; } }; @@ -6328,6 +6336,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void cleanupDisabledPackageComponents( String packageName, Set<String> disabledClasses, int userId, boolean booted) { synchronized (mGlobalLock) { + // In case if setWindowManager hasn't been called yet when booting. + if (mRootWindowContainer == null) return; // Clean-up disabled activities. if (mRootWindowContainer.finishDisabledPackageActivities( packageName, disabledClasses, true /* doit */, false /* evenPersistent */, diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java index 2edb082ab8df..78522498987c 100644 --- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java +++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java @@ -20,41 +20,73 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.internal.R.bool.config_unfoldTransitionEnabled; import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY; +import static com.android.server.wm.DeviceStateController.DeviceState.FOLDED; +import static com.android.server.wm.DeviceStateController.DeviceState.HALF_FOLDED; +import static com.android.server.wm.DeviceStateController.DeviceState.OPEN; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.graphics.Rect; import android.window.DisplayAreaInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wm.DeviceStateController.DeviceState; + public class PhysicalDisplaySwitchTransitionLauncher { private final DisplayContent mDisplayContent; - private final WindowManagerService mService; + private final ActivityTaskManagerService mAtmService; + private final Context mContext; private final TransitionController mTransitionController; /** - * If on a foldable device represents whether the device is folded or not + * If on a foldable device represents whether we need to show unfold animation when receiving + * a physical display switch event + */ + private boolean mShouldRequestTransitionOnDisplaySwitch = false; + /** + * Current device state from {@link android.hardware.devicestate.DeviceStateManager} */ - private boolean mIsFolded; + private DeviceState mDeviceState = DeviceState.UNKNOWN; private Transition mTransition; public PhysicalDisplaySwitchTransitionLauncher(DisplayContent displayContent, TransitionController transitionController) { + this(displayContent, displayContent.mWmService.mAtmService, + displayContent.mWmService.mContext, transitionController); + } + + @VisibleForTesting + public PhysicalDisplaySwitchTransitionLauncher(DisplayContent displayContent, + ActivityTaskManagerService service, Context context, + TransitionController transitionController) { mDisplayContent = displayContent; - mService = displayContent.mWmService; + mAtmService = service; + mContext = context; mTransitionController = transitionController; } /** * Called by the DeviceStateManager callback when the state changes. */ - void foldStateChanged(DeviceStateController.DeviceState newDeviceState) { - // Ignore transitions to/from half-folded. - if (newDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) return; - mIsFolded = newDeviceState == DeviceStateController.DeviceState.FOLDED; + void foldStateChanged(DeviceState newDeviceState) { + boolean isUnfolding = mDeviceState == FOLDED + && (newDeviceState == HALF_FOLDED || newDeviceState == OPEN); + + if (isUnfolding) { + // Request transition only if we are unfolding the device + mShouldRequestTransitionOnDisplaySwitch = true; + } else if (newDeviceState != HALF_FOLDED && newDeviceState != OPEN) { + // Cancel the transition request in case if we are folding or switching to back + // to the rear display before the displays got switched + mShouldRequestTransitionOnDisplaySwitch = false; + } + + mDeviceState = newDeviceState; } /** @@ -62,12 +94,12 @@ public class PhysicalDisplaySwitchTransitionLauncher { */ public void requestDisplaySwitchTransitionIfNeeded(int displayId, int oldDisplayWidth, int oldDisplayHeight, int newDisplayWidth, int newDisplayHeight) { + if (!mShouldRequestTransitionOnDisplaySwitch) return; if (!mTransitionController.isShellTransitionsEnabled()) return; if (!mDisplayContent.getLastHasContent()) return; - boolean shouldRequestUnfoldTransition = !mIsFolded - && mService.mContext.getResources().getBoolean(config_unfoldTransitionEnabled) - && ValueAnimator.areAnimatorsEnabled(); + boolean shouldRequestUnfoldTransition = mContext.getResources() + .getBoolean(config_unfoldTransitionEnabled) && ValueAnimator.areAnimatorsEnabled(); if (!shouldRequestUnfoldTransition) { return; @@ -91,6 +123,8 @@ public class PhysicalDisplaySwitchTransitionLauncher { mDisplayContent.mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); mTransition = t; } + + mShouldRequestTransitionOnDisplaySwitch = false; } /** @@ -118,7 +152,7 @@ public class PhysicalDisplaySwitchTransitionLauncher { if (mTransition == null) return; if (transaction != null) { - mService.mAtmService.mWindowOrganizerController.applyTransaction(transaction); + mAtmService.mWindowOrganizerController.applyTransaction(transaction); } markTransitionAsReady(); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 80f918b9bcab..50bf38b8ce16 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -963,7 +963,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return false; } return mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs, - false /* fromClient */); + false /* fromClient */, true /* isAutoEnter */); } // Legacy pip-entry (not via isAutoEnterEnabled). @@ -2267,7 +2267,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Display won't be rotated for multi window Task, so the fixed rotation // won't be applied. This can happen when the windowing mode is changed // before the previous fixed rotation is applied. - && !task.inMultiWindowMode()) { + && (!task.inMultiWindowMode() || !topRunningActivity.inMultiWindowMode())) { // If Activity is in fixed rotation, its will be applied with the next rotation, // when the Task is still in the previous rotation. final int taskRotation = task.getWindowConfiguration().getDisplayRotation(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f9b6fc10360d..40c6c464f433 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3915,8 +3915,9 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); if (displayContent == null) { - throw new IllegalStateException("No touch mode is defined for displayId {" - + displayId + "}"); + throw new IllegalStateException("Failed to retrieve the touch mode state for" + + "display {" + displayId + "}: display is not registered in " + + "WindowRootContainer"); } return displayContent.isInTouchMode(); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index cf49dcf8004e..0c4830afafcc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -684,6 +684,38 @@ final class DevicePolicyEngine { } } + <V> void transferPolicies(EnforcingAdmin oldAdmin, EnforcingAdmin newAdmin) { + Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); + for (PolicyKey policy : globalPolicies) { + PolicyState<?> policyState = mGlobalPolicies.get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { + PolicyDefinition<V> policyDefinition = + (PolicyDefinition<V>) policyState.getPolicyDefinition(); + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); + setGlobalPolicy(policyDefinition, newAdmin, policyValue); + } + } + + for (int i = 0; i < mLocalPolicies.size(); i++) { + int userId = mLocalPolicies.keyAt(i); + Set<PolicyKey> localPolicies = new HashSet<>( + mLocalPolicies.get(userId).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { + PolicyDefinition<V> policyDefinition = + (PolicyDefinition<V>) policyState.getPolicyDefinition(); + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); + setLocalPolicy(policyDefinition, newAdmin, policyValue, userId); + } + } + } + + removePoliciesForAdmin(oldAdmin); + } + private Set<UserRestrictionPolicyKey> getUserRestrictionPolicyKeysForAdminLocked( Map<PolicyKey, PolicyState<?>> policies, EnforcingAdmin admin) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index dedc0734cd04..3578b16d62c6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3882,6 +3882,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver); final int oldAdminUid = adminToTransfer.getUid(); + if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { + EnforcingAdmin oldAdmin = + EnforcingAdmin.createEnterpriseEnforcingAdmin( + outgoingReceiver, userHandle, adminToTransfer); + EnforcingAdmin newAdmin = + EnforcingAdmin.createEnterpriseEnforcingAdmin( + incomingReceiver, userHandle, adminToTransfer); + + mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin); + } + adminToTransfer.transfer(incomingDeviceInfo); policy.mAdminMap.remove(outgoingReceiver); policy.mAdminMap.put(incomingReceiver, adminToTransfer); @@ -6051,7 +6062,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void lockNow(int flags, String callerPackageName, boolean parent) { CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(callerPackageName); } else { caller = getCallerIdentity(); @@ -6063,7 +6074,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ActiveAdmin admin; // Make sure the caller has any active admin with the right policy or // the required permission. - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { admin = enforcePermissionAndGetEnforcingAdmin( /* admin= */ null, /* permission= */ MANAGE_DEVICE_POLICY_LOCK, @@ -8917,13 +8928,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // The effect of this policy is device-wide. enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -8951,13 +8962,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforceCanQuery(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL); } else { Objects.requireNonNull(who, "ComponentName is null"); @@ -8980,13 +8991,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // The effect of this policy is device-wide. EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, @@ -9026,13 +9037,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // The effect of this policy is device-wide. enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -9335,7 +9346,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); @@ -9345,7 +9356,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userHandle = caller.getUserId(); int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle; synchronized (getLockObject()) { - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { // SUPPORT USES_POLICY_DISABLE_KEYGUARD_FEATURES EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_KEYGUARD, caller.getPackageName(), @@ -9424,13 +9435,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (who != null) { - if (isPermissionCheckFlagEnabled()) { - EnforcingAdmin admin = getEnforcingAdminForCaller( - who, who.getPackageName()); + if (isUnicornFlagEnabled()) { + EnforcingAdmin admin = getEnforcingAdminForPackage( + who, who.getPackageName(), userHandle); Integer features = mDevicePolicyEngine.getLocalPolicySetByAdmin( PolicyDefinition.KEYGUARD_DISABLED_FEATURES, admin, affectedUserId); + return features == null ? 0 : features; } else { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); @@ -9438,7 +9450,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { Integer features = mDevicePolicyEngine.getResolvedPolicy( PolicyDefinition.KEYGUARD_DISABLED_FEATURES, affectedUserId); @@ -9999,10 +10011,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "clearDeviceOwner can only be called by the device owner"); } enforceUserUnlocked(deviceOwnerUserId); - DevicePolicyData policy = getUserData(deviceOwnerUserId); - if (policy.mPasswordTokenHandle != 0) { - mLockPatternUtils.removeEscrowToken(policy.mPasswordTokenHandle, deviceOwnerUserId); - } final ActiveAdmin admin = getDeviceOwnerAdminLocked(); mInjector.binderWithCleanCallingIdentity(() -> { @@ -10057,6 +10065,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } final DevicePolicyData policyData = getUserData(userId); policyData.mCurrentInputMethodSet = false; + if (policyData.mPasswordTokenHandle != 0) { + mLockPatternUtils.removeEscrowToken(policyData.mPasswordTokenHandle, userId); + policyData.mPasswordTokenHandle = 0; + } saveSettingsLocked(userId); mPolicyCache.onUserRemoved(userId); final DevicePolicyData systemPolicyData = getUserData(UserHandle.USER_SYSTEM); @@ -11637,7 +11649,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(who, callerPackage); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, @@ -13064,7 +13076,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, @@ -13134,7 +13146,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(who, callerPackage); ActiveAdmin admin; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_PACKAGE_STATE, @@ -13231,7 +13243,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforcePermission( MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), @@ -13837,7 +13849,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean hidden, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { // TODO: We need to ensure the delegate with DELEGATION_PACKAGE_ACCESS can do this enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); } else { @@ -13856,7 +13868,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean result; synchronized (getLockObject()) { if (parent) { - if (!isPermissionCheckFlagEnabled()) { + if (!isPolicyEngineForFinanceFlagEnabled()) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice( caller.getUserId()) && isManagedProfile(caller.getUserId())); @@ -13873,7 +13885,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)", packageName, hidden, userId); } - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); mDevicePolicyEngine.setLocalPolicy( PolicyDefinition.APPLICATION_HIDDEN(packageName), @@ -13912,7 +13924,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String packageName, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { // TODO: Also support DELEGATION_PACKAGE_ACCESS enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); } else { @@ -13924,7 +13936,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (parent) { - if (!isPermissionCheckFlagEnabled()) { + if (!isPolicyEngineForFinanceFlagEnabled()) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()) && isManagedProfile(caller.getUserId())); @@ -14116,13 +14128,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforceMaxStringLength(accountType, "account type"); CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } synchronized (getLockObject()) { - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { int affectedUser = getAffectedUser(parent); EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, @@ -14185,7 +14197,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { CallerIdentity caller; Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); final ArraySet<String> resultSet = new ArraySet<>(); - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { int affectedUser = parent ? getProfileParentId(userId) : userId; caller = getCallerIdentity(callerPackageName); if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, @@ -15556,12 +15568,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public boolean setStatusBarDisabled(ComponentName who, String callerPackageName, boolean disabled) { CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforcePermission(MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -15572,7 +15584,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = caller.getUserId(); synchronized (getLockObject()) { - if (!isPermissionCheckFlagEnabled()) { + if (!isUnicornFlagEnabled()) { Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), "Admin " + who + " is neither the device owner or affiliated " + "user's profile owner."); @@ -15631,7 +15643,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean isStatusBarDisabled(String callerPackage) { final CallerIdentity caller = getCallerIdentity(callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforceCanQuery( MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), caller.getUserId()); } else { @@ -15641,7 +15653,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = caller.getUserId(); synchronized (getLockObject()) { - if (!isPermissionCheckFlagEnabled()) { + if (!isUnicornFlagEnabled()) { Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), "Admin " + callerPackage + " is neither the device owner or affiliated user's profile owner."); @@ -16802,7 +16814,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } EnforcingAdmin enforcingAdmin; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, @@ -16973,7 +16985,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public int getPermissionGrantState(ComponentName admin, String callerPackage, String packageName, String permission) throws RemoteException { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { enforceCanQuery(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, caller.getPackageName(), caller.getUserId()); } else { @@ -19111,14 +19123,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalArgumentException("token must be at least 32-byte long"); } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); } final int userId = caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19174,7 +19186,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); @@ -19182,7 +19194,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userId = caller.getUserId(); boolean result = false; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19221,14 +19233,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); } int userId = caller.getUserId(); - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19270,7 +19282,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Objects.requireNonNull(token); CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); @@ -19280,7 +19292,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean result = false; final String password = passwordOrNull != null ? passwordOrNull : ""; - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19311,7 +19323,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (result) { - if (isPermissionCheckFlagEnabled()) { + if (isUnicornFlagEnabled()) { DevicePolicyEventLogger .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN) .setAdmin(callerPackageName) @@ -23569,6 +23581,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin); } + private EnforcingAdmin getEnforcingAdminForPackage(@Nullable ComponentName who, + String packageName, int userId) { + ActiveAdmin admin; + if (who != null) { + if (isDeviceOwner(who, userId) || isProfileOwner(who, userId)) { + synchronized (getLockObject()) { + admin = getActiveAdminUncheckedLocked(who, userId); + } + if (admin != null) { + return EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userId, admin); + } + } else { + // Check for non-DPC active admins. + admin = getActiveAdminUncheckedLocked(who, userId); + if (admin != null) { + return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin); + } + } + } + + admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId); + return EnforcingAdmin.createEnforcingAdmin(packageName, userId, admin); + } + private int getAffectedUser(boolean calledOnParent) { int callingUserId = mInjector.userHandleGetCallingUserId(); return calledOnParent ? getProfileParentId(callingUserId) : callingUserId; @@ -23624,6 +23660,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DEFAULT_KEEP_PROFILES_RUNNING_FLAG); } + private boolean isUnicornFlagEnabled() { + return false; + } + private boolean isWorkProfileTelephonyEnabled() { return isWorkProfileTelephonyDevicePolicyManagerFlagEnabled() && isWorkProfileTelephonySubscriptionManagerFlagEnabled(); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java index 941a3a419d59..3faf394fc534 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java @@ -72,18 +72,28 @@ public class DisplayWhiteBalanceTintControllerTest { mResources = InstrumentationRegistry.getContext().getResources(); // These Resources are common to all tests. - doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMin)) + doReturn(4000) .when(mMockedResources) .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMin); - doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMax)) + doReturn(8000) .when(mMockedResources) .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureMax); - doReturn(mResources.getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault)) + doReturn(6500) .when(mMockedResources) .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault); - doReturn(mResources.getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite)) - .when(mMockedResources) - .getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite); + doReturn(new String[] {"0.950456", "1.000000", "1.089058"}) + .when(mMockedResources) + .getStringArray(R.array.config_displayWhiteBalanceDisplayNominalWhite); + doReturn(6500) + .when(mMockedResources) + .getInteger(R.integer.config_displayWhiteBalanceDisplayNominalWhiteCct); + doReturn(new int[] {0}) + .when(mMockedResources) + .getIntArray(R.array.config_displayWhiteBalanceDisplaySteps); + doReturn(new int[] {20}) + .when(mMockedResources) + .getIntArray(R.array.config_displayWhiteBalanceDisplayRangeMinimums); + doReturn(mMockedResources).when(mMockedContext).getResources(); mDisplayToken = new Binder(); @@ -195,7 +205,7 @@ public class DisplayWhiteBalanceTintControllerTest { * Matrix should match the precalculated one for given cct and display primaries. */ @Test - public void displayWhiteBalance_validateTransformMatrix() { + public void displayWhiteBalance_getAndSetMatrix_validateTransformMatrix() { DisplayPrimaries displayPrimaries = new DisplayPrimaries(); displayPrimaries.red = new CieXyz(); displayPrimaries.red.X = 0.412315f; @@ -223,10 +233,12 @@ public class DisplayWhiteBalanceTintControllerTest { final int cct = 6500; mDisplayWhiteBalanceTintController.setMatrix(cct); + mDisplayWhiteBalanceTintController.setAppliedCct( + mDisplayWhiteBalanceTintController.getTargetCct()); + assertWithMessage("Failed to set temperature") .that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature) .isEqualTo(cct); - float[] matrixDwb = mDisplayWhiteBalanceTintController.getMatrix(); final float[] expectedMatrixDwb = { 0.971848f, -0.001421f, 0.000491f, 0.0f, @@ -238,6 +250,54 @@ public class DisplayWhiteBalanceTintControllerTest { 1e-6f /* tolerance */); } + /** + * Matrix should match the precalculated one for given cct and display primaries. + */ + @Test + public void displayWhiteBalance_targetApplied_validateTransformMatrix() { + DisplayPrimaries displayPrimaries = new DisplayPrimaries(); + displayPrimaries.red = new CieXyz(); + displayPrimaries.red.X = 0.412315f; + displayPrimaries.red.Y = 0.212600f; + displayPrimaries.red.Z = 0.019327f; + displayPrimaries.green = new CieXyz(); + displayPrimaries.green.X = 0.357600f; + displayPrimaries.green.Y = 0.715200f; + displayPrimaries.green.Z = 0.119200f; + displayPrimaries.blue = new CieXyz(); + displayPrimaries.blue.X = 0.180500f; + displayPrimaries.blue.Y = 0.072200f; + displayPrimaries.blue.Z = 0.950633f; + displayPrimaries.white = new CieXyz(); + displayPrimaries.white.X = 0.950456f; + displayPrimaries.white.Y = 1.000000f; + displayPrimaries.white.Z = 1.089058f; + when(mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY)) + .thenReturn(displayPrimaries); + + setUpTintController(); + assertWithMessage("Setup with valid SurfaceControl failed") + .that(mDisplayWhiteBalanceTintController.mSetUp) + .isTrue(); + + final int cct = 6500; + mDisplayWhiteBalanceTintController.setTargetCct(cct); + final float[] matrixDwb = mDisplayWhiteBalanceTintController.computeMatrixForCct(cct); + mDisplayWhiteBalanceTintController.setAppliedCct(cct); + + assertWithMessage("Failed to set temperature") + .that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .isEqualTo(cct); + final float[] expectedMatrixDwb = { + 0.971848f, -0.001421f, 0.000491f, 0.0f, + 0.028193f, 0.945798f, 0.003207f, 0.0f, + -0.000042f, -0.000989f, 0.988659f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + assertArrayEquals("Unexpected DWB matrix", expectedMatrixDwb, matrixDwb, + 1e-6f /* tolerance */); + } + private void setUpTintController() { mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController( mDisplayManagerInternal); diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java index f788c92b24b2..46974cf72381 100644 --- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java @@ -28,6 +28,8 @@ import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; +import static com.android.server.am.ProcessList.UNKNOWN_ADJ; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -81,7 +83,7 @@ public class UidObserverControllerTest { public void testEnqueueUidChange() { int change = mUidObserverController.enqueueUidChange(null, TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, - PROCESS_CAPABILITY_ALL, 0, false); + UNKNOWN_ADJ, PROCESS_CAPABILITY_ALL, 0, false); assertEquals("expected=ACTIVE,actual=" + changeToStr(change), UidRecord.CHANGE_ACTIVE, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, @@ -91,8 +93,8 @@ public class UidObserverControllerTest { final ChangeRecord record2 = new ChangeRecord(); change = mUidObserverController.enqueueUidChange(record2, TEST_UID2, - UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, - 99, true); + UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, UNKNOWN_ADJ, + PROCESS_CAPABILITY_NONE, 99, true); assertEquals("expected=ACTIVE,actual=" + changeToStr(change), UidRecord.CHANGE_CACHED, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, @@ -101,7 +103,8 @@ public class UidObserverControllerTest { PROCESS_CAPABILITY_NONE, 99, true, record2); change = mUidObserverController.enqueueUidChange(record1, TEST_UID1, - UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false); + UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, UNKNOWN_ADJ, + PROCESS_CAPABILITY_ALL, 0, false); assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change), UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index d12741ac8bd6..317fd58e0c76 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -100,6 +100,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.am.UserState.KeyEvictedCallback; +import com.android.server.pm.UserJourneyLogger; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.wm.WindowManagerService; @@ -199,6 +200,7 @@ public class UserControllerTest { mUserController.setAllowUserUnlocking(true); setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated= */ true, null); + mInjector.mRelevantUser = null; }); } @@ -232,6 +234,25 @@ public class UserControllerTest { } @Test + public void testStartUser_background_duringBootHsum() { + mockIsHeadlessSystemUserMode(true); + mUserController.setAllowUserUnlocking(false); + mInjector.mRelevantUser = TEST_USER_ID; + boolean started = mUserController.startUser(TEST_USER_ID, USER_START_MODE_BACKGROUND); + assertWithMessage("startUser(%s, foreground=false)", TEST_USER_ID).that(started).isTrue(); + + // ACTION_LOCKED_BOOT_COMPLETED not sent yet + startUserAssertions(newArrayList(Intent.ACTION_USER_STARTED, Intent.ACTION_USER_STARTING), + START_BACKGROUND_USER_MESSAGE_CODES); + + mUserController.onBootComplete(null); + + startUserAssertions(newArrayList(Intent.ACTION_USER_STARTED, Intent.ACTION_USER_STARTING, + Intent.ACTION_LOCKED_BOOT_COMPLETED), + START_BACKGROUND_USER_MESSAGE_CODES); + } + + @Test public void testStartUser_sendsNoBroadcastsForSystemUserInNonHeadlessMode() { setUpUser(SYSTEM_USER_ID, UserInfo.FLAG_SYSTEM, /* preCreated= */ false, UserManager.USER_TYPE_FULL_SYSTEM); @@ -1074,8 +1095,12 @@ public class UserControllerTest { private final KeyguardManager mKeyguardManagerMock; private final LockPatternUtils mLockPatternUtilsMock; + private final UserJourneyLogger mUserJourneyLoggerMock; + private final Context mCtx; + private Integer mRelevantUser; + TestInjector(Context ctx) { super(null); mCtx = ctx; @@ -1090,6 +1115,7 @@ public class UserControllerTest { mKeyguardManagerMock = mock(KeyguardManager.class); when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true); mLockPatternUtilsMock = mock(LockPatternUtils.class); + mUserJourneyLoggerMock = mock(UserJourneyLogger.class); } @Override @@ -1162,7 +1188,9 @@ public class UserControllerTest { boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId) { Log.i(TAG, "broadcastIntentLocked " + intent); - mSentIntents.add(intent); + if (mRelevantUser == null || mRelevantUser == userId || userId == UserHandle.USER_ALL) { + mSentIntents.add(intent); + } return 0; } @@ -1220,6 +1248,11 @@ public class UserControllerTest { void onSystemUserVisibilityChanged(boolean visible) { Log.i(TAG, "onSystemUserVisibilityChanged(" + visible + ")"); } + + @Override + protected UserJourneyLogger getUserJourneyLogger() { + return mUserJourneyLoggerMock; + } } private static class TestHandler extends Handler { diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java index c5a9af7d909d..dcd06c9cc716 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java @@ -30,7 +30,7 @@ public class CallMetadataSyncDataTest { @Test public void call_writeToParcel_fromParcel_reconstructsSuccessfully() { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); - final long id = 5; + final String id = "5"; final String callerId = "callerId"; final byte[] appIcon = "appIcon".getBytes(); final String appName = "appName"; diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java index a488ab446aff..afddf3c9be29 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java @@ -47,24 +47,24 @@ public class CallMetadataSyncInCallServiceTest { @Test public void getCallForId_invalid() { - when(mMockCrossDeviceCall.getId()).thenReturn(-1L); - final CrossDeviceCall call = mSyncInCallService.getCallForId(-1L, + when(mMockCrossDeviceCall.getId()).thenReturn(null); + final CrossDeviceCall call = mSyncInCallService.getCallForId(null, List.of(mMockCrossDeviceCall)); assertWithMessage("Unexpectedly found a match for call id").that(call).isNull(); } @Test public void getCallForId_noMatch() { - when(mMockCrossDeviceCall.getId()).thenReturn(5L); - final CrossDeviceCall call = mSyncInCallService.getCallForId(1L, + when(mMockCrossDeviceCall.getId()).thenReturn("123abc"); + final CrossDeviceCall call = mSyncInCallService.getCallForId("abc123", List.of(mMockCrossDeviceCall)); assertWithMessage("Unexpectedly found a match for call id").that(call).isNull(); } @Test public void getCallForId_hasMatch() { - when(mMockCrossDeviceCall.getId()).thenReturn(5L); - final CrossDeviceCall call = mSyncInCallService.getCallForId(5L, + when(mMockCrossDeviceCall.getId()).thenReturn("123abc"); + final CrossDeviceCall call = mSyncInCallService.getCallForId("123abc", List.of(mMockCrossDeviceCall)); assertWithMessage("Unexpectedly did not find a match for call id").that(call).isNotNull(); } diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java index 9d42a5bf05d2..5a0646c0e0e5 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java @@ -62,9 +62,9 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT, - android.companion.Telecom.Call.SILENCE)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT, + android.companion.Telecom.SILENCE)); } @Test @@ -77,9 +77,9 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE, + android.companion.Telecom.PUT_ON_HOLD)); } @Test @@ -92,8 +92,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ON_HOLD); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.TAKE_OFF_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.TAKE_OFF_HOLD)); } @Test @@ -106,8 +106,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE)); } @Test @@ -120,8 +120,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.PUT_ON_HOLD)); } @Test @@ -134,17 +134,17 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status for ringing state").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING); assertWithMessage("Wrong controls for ringing state").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT, - android.companion.Telecom.Call.SILENCE)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT, + android.companion.Telecom.SILENCE)); crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE)); assertWithMessage("Wrong status for active state").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls for active state").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE, + android.companion.Telecom.PUT_ON_HOLD)); } @Test @@ -158,8 +158,8 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING_SILENCED); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT)); } @Test @@ -173,9 +173,9 @@ public class CrossDeviceCallTest { assertWithMessage("Wrong status").that(crossDeviceCall.getStatus()) .isEqualTo(android.companion.Telecom.Call.ONGOING); assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.END, - android.companion.Telecom.Call.MUTE, - android.companion.Telecom.Call.PUT_ON_HOLD)); + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE, + android.companion.Telecom.PUT_ON_HOLD)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java index eec026ccfc8a..25b0ae486230 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java @@ -81,7 +81,7 @@ public class CrossDeviceSyncControllerTest { @Test public void processTelecomDataFromSync_createCallUpdateMessage_hasCalls() { - when(mMockCrossDeviceCall.getId()).thenReturn(5L); + when(mMockCrossDeviceCall.getId()).thenReturn("123abc"); final String callerId = "Firstname Lastname"; when(mMockCrossDeviceCall.getReadableCallerId(anyBoolean())).thenReturn(callerId); final String appName = "AppName"; @@ -90,9 +90,9 @@ public class CrossDeviceSyncControllerTest { when(mMockCrossDeviceCall.getCallingAppIcon()).thenReturn(appIcon.getBytes()); when(mMockCrossDeviceCall.getStatus()).thenReturn(android.companion.Telecom.Call.RINGING); final Set<Integer> controls = Set.of( - android.companion.Telecom.Call.ACCEPT, - android.companion.Telecom.Call.REJECT, - android.companion.Telecom.Call.SILENCE); + android.companion.Telecom.ACCEPT, + android.companion.Telecom.REJECT, + android.companion.Telecom.SILENCE); when(mMockCrossDeviceCall.getControls()).thenReturn(controls); final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage( new HashSet<>(List.of(mMockCrossDeviceCall)), @@ -103,35 +103,33 @@ public class CrossDeviceSyncControllerTest { callMetadataSyncData.getCalls()).hasSize(1); final CallMetadataSyncData.Call call = callMetadataSyncData.getCalls().stream().findAny().orElseThrow(); - assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L); + assertWithMessage("Wrong id").that(call.getId()).isEqualTo("123abc"); assertWithMessage("Wrong app icon").that(new String(call.getAppIcon())).isEqualTo(appIcon); assertWithMessage("Wrong app name").that(call.getAppName()).isEqualTo(appName); assertWithMessage("Wrong caller id").that(call.getCallerId()).isEqualTo(callerId); assertWithMessage("Wrong status").that(call.getStatus()) .isEqualTo(android.companion.Telecom.Call.RINGING); assertWithMessage("Wrong controls").that(call.getControls()).isEqualTo(controls); - assertWithMessage("Unexpectedly has requests").that( - callMetadataSyncData.getRequests()).isEmpty(); } @Test public void processTelecomDataFromMessage_createCallControlMessage_hasCallControlRequest() { final byte[] data = CrossDeviceSyncController.createCallControlMessage( - /* callId= */ 5L, /* status= */ android.companion.Telecom.Call.ACCEPT); + /* callId= */ "123abc", /* status= */ android.companion.Telecom.ACCEPT); final CallMetadataSyncData callMetadataSyncData = mCrossDeviceSyncController.processTelecomDataFromSync(data); assertWithMessage("Wrong number of requests").that( callMetadataSyncData.getRequests()).hasSize(1); final CallMetadataSyncData.Call call = callMetadataSyncData.getRequests().stream().findAny().orElseThrow(); - assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L); + assertWithMessage("Wrong id").that(call.getId()).isEqualTo("123abc"); assertWithMessage("Wrong app icon").that(call.getAppIcon()).isNull(); assertWithMessage("Wrong app name").that(call.getAppName()).isNull(); assertWithMessage("Wrong caller id").that(call.getCallerId()).isNull(); assertWithMessage("Wrong status").that(call.getStatus()) .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS); assertWithMessage("Wrong controls").that(call.getControls()) - .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT)); + .isEqualTo(Set.of(android.companion.Telecom.ACCEPT)); assertWithMessage("Unexpectedly has active calls").that( callMetadataSyncData.getCalls()).isEmpty(); } 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 1e342f580745..57755a9525fc 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1512,6 +1512,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Validates that when the device owner is removed, the reset password token is cleared */ @Test + @Ignore("b/277916462") public void testClearDeviceOwner_clearResetPasswordToken() throws Exception { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -2602,6 +2603,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetApplicationHiddenWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2627,6 +2629,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetApplicationHiddenWithPOOfOrganizationOwnedDevice() throws Exception { final int MANAGED_PROFILE_USER_ID = CALLER_USER_HANDLE; final int MANAGED_PROFILE_ADMIN_UID = @@ -4373,6 +4376,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4384,6 +4388,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; setupProfileOwnerOnUser0(); @@ -4395,6 +4400,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, @@ -4404,6 +4410,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -5377,6 +5384,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testResetPasswordWithToken() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5411,6 +5419,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void resetPasswordWithToken_NumericPin() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5431,6 +5440,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void resetPasswordWithToken_EmptyPassword() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -7251,6 +7261,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testCanProfileOwnerResetPasswordWhenLocked() throws Exception { setDeviceEncryptionPerUser(); setupProfileOwner(); @@ -7314,6 +7325,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAccountTypesWithManagementDisabledOnManagedProfile() throws Exception { setupProfileOwner(); @@ -7333,6 +7345,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile() throws Exception { mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS); diff --git a/services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java new file mode 100644 index 000000000000..b96666ae40a3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.color; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CctEvaluatorTest { + + @Test + public void noEntriesInParallelArrays_setsEverythingToOne() { + final CctEvaluator evaluator = new CctEvaluator(0, 5, new int[]{}, new int[]{}); + assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{1, 1, 1, 1, 1, 1}); + assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo( + new int[]{0, 1, 2, 3, 4, 5}); + } + + @Test + public void unevenNumberOfEntriesInParallelArrays_setsEverythingToOne() { + final CctEvaluator evaluator = new CctEvaluator(0, 5, new int[]{0}, new int[]{}); + assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{1, 1, 1, 1, 1, 1}); + assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo( + new int[]{0, 1, 2, 3, 4, 5}); + } + + @Test + public void singleEntryInParallelArray_computesCorrectly() { + final CctEvaluator evaluator = new CctEvaluator(0, 5, new int[]{0}, new int[]{2}); + assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{2, 2, 2, 2, 2, 2}); + assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo( + new int[]{0, 0, 2, 2, 4, 4}); + } + + @Test + public void minimumIsBelowFirstRange_computesCorrectly() { + final CctEvaluator evaluator = new CctEvaluator(3000, 3005, new int[]{3002}, + new int[]{20}); + assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo(new int[]{20, 20, 20, 20, 20, 20}); + assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo( + new int[]{3000, 3000, 3000, 3000, 3000, 3000}); + } + + @Test + public void minimumIsAboveFirstRange_computesCorrectly() { + final CctEvaluator evaluator = new CctEvaluator(3000, 3008, new int[]{3002}, + new int[]{20}); + assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo( + new int[]{20, 20, 20, 20, 20, 20, 20, 20, 20}); + assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo( + new int[]{3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000}); + } + + @Test + public void multipleStepsStartsAtThreshold_computesCorrectly() { + final CctEvaluator evaluator = new CctEvaluator(5, 20, new int[]{0, 4, 5, 10, 18}, + new int[]{11, 7, 2, 15, 9}); + assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo( + new int[]{2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 9, 9, 9}); + assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo( + new int[]{5, 5, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 18, 18}); + } + + @Test + public void multipleStepsStartsInBetween_computesCorrectly() { + final CctEvaluator evaluator = new CctEvaluator(4, 20, new int[]{0, 5, 10, 18}, + new int[]{14, 2, 15, 9}); + assertThat(evaluator.mStepsAtOffsetCcts).isEqualTo( + new int[]{14, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 9, 9, 9}); + assertThat(evaluator.mSteppedCctsAtOffsetCcts).isEqualTo( + new int[]{4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 18, 18}); + } +} diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt index d0d28c3f7ec0..55c45df2b844 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -85,6 +85,9 @@ class KeyboardLayoutManagerTests { const val DEVICE_ID = 1 const val VENDOR_SPECIFIC_DEVICE_ID = 2 const val ENGLISH_DVORAK_DEVICE_ID = 3 + const val ENGLISH_QWERTY_DEVICE_ID = 4 + const val DEFAULT_VENDOR_ID = 123 + const val DEFAULT_PRODUCT_ID = 456 const val USER_ID = 4 const val IME_ID = "ime_id" const val PACKAGE_NAME = "KeyboardLayoutManagerTests" @@ -122,6 +125,7 @@ class KeyboardLayoutManagerTests { private lateinit var keyboardDevice: InputDevice private lateinit var vendorSpecificKeyboardDevice: InputDevice private lateinit var englishDvorakKeyboardDevice: InputDevice + private lateinit var englishQwertyKeyboardDevice: InputDevice @Before fun setup() { @@ -150,17 +154,26 @@ class KeyboardLayoutManagerTests { Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) - keyboardDevice = createKeyboard(DEVICE_ID, 0, 0, "", "") + keyboardDevice = createKeyboard(DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, "", "") vendorSpecificKeyboardDevice = createKeyboard(VENDOR_SPECIFIC_DEVICE_ID, 1, 1, "", "") - englishDvorakKeyboardDevice = - createKeyboard(ENGLISH_DVORAK_DEVICE_ID, 0, 0, "en", "dvorak") + englishDvorakKeyboardDevice = createKeyboard(ENGLISH_DVORAK_DEVICE_ID, DEFAULT_VENDOR_ID, + DEFAULT_PRODUCT_ID, "en", "dvorak") + englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID, + DEFAULT_PRODUCT_ID, "en", "qwerty") Mockito.`when`(iInputManager.inputDeviceIds) - .thenReturn(intArrayOf(DEVICE_ID, VENDOR_SPECIFIC_DEVICE_ID, ENGLISH_DVORAK_DEVICE_ID)) + .thenReturn(intArrayOf( + DEVICE_ID, + VENDOR_SPECIFIC_DEVICE_ID, + ENGLISH_DVORAK_DEVICE_ID, + ENGLISH_QWERTY_DEVICE_ID + )) Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) Mockito.`when`(iInputManager.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID)) .thenReturn(vendorSpecificKeyboardDevice) Mockito.`when`(iInputManager.getInputDevice(ENGLISH_DVORAK_DEVICE_ID)) .thenReturn(englishDvorakKeyboardDevice) + Mockito.`when`(iInputManager.getInputDevice(ENGLISH_QWERTY_DEVICE_ID)) + .thenReturn(englishQwertyKeyboardDevice) } private fun setupBroadcastReceiver() { @@ -778,14 +791,23 @@ class KeyboardLayoutManagerTests { @Test fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withHwLanguageTagAndLayoutType() { NewSettingsApiFlag(true).use { - // Should return English dvorak even if IME current layout is qwerty, since HW says the + val frenchSubtype = createImeSubtypeForLanguageTagAndLayoutType("fr", "azerty") + // Should return English dvorak even if IME current layout is French, since HW says the // keyboard is a Dvorak keyboard assertCorrectLayout( englishDvorakKeyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("en", "qwerty"), + frenchSubtype, createLayoutDescriptor("keyboard_layout_english_us_dvorak") ) + // Back to back changing HW keyboards with same product and vendor ID but different + // language and layout type should configure the layouts correctly. + assertCorrectLayout( + englishQwertyKeyboardDevice, + frenchSubtype, + createLayoutDescriptor("keyboard_layout_english_us") + ) + // Fallback to IME information if the HW provided layout script is incompatible with the // provided IME subtype assertCorrectLayout( diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 01e56a0ddae6..1cfaf7c83584 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -228,6 +228,15 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { }); } + public void testShortcutIdTruncated() { + ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), + "s".repeat(Short.MAX_VALUE)).build(); + + assertTrue( + "id must be truncated to MAX_ID_LENGTH", + si.getId().length() <= ShortcutInfo.MAX_ID_LENGTH); + } + public void testShortcutInfoParcel() { setCaller(CALLING_PACKAGE_1, USER_10); ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext) diff --git a/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java new file mode 100644 index 000000000000..20e2692cb747 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/UserJourneyLoggerTest.java @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INCOMPLETE_OR_TIMEOUT; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_NULL_USER_INFO; +import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_BEGIN; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_CANCEL; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_ERROR; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_FINISH; +import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_NONE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_CREATE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_REMOVE; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START; +import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_STOP; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_CREATE_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_REMOVE_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_REVOKE_ADMIN; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_START_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_STOP_USER; +import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.pm.UserInfo; +import android.platform.test.annotations.Presubmit; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.FrameworkStatsLog; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class UserJourneyLoggerTest { + + public static final int FULL_USER_ADMIN_FLAG = 0x00000402; + private UserJourneyLogger mUserJourneyLogger; + + @Before + public void setup() throws Exception { + mUserJourneyLogger = spy(new UserJourneyLogger()); + } + + @Test + public void testUserStartLifecycleJourneyReported() { + final UserLifecycleJourneyReportedCaptor report1 = new UserLifecycleJourneyReportedCaptor(); + final UserJourneyLogger.UserJourneySession session = new UserJourneyLogger + .UserJourneySession(10, USER_JOURNEY_USER_START); + + report1.captureLogAndAssert(mUserJourneyLogger, session, + USER_JOURNEY_USER_START, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, 1, + ERROR_CODE_UNSPECIFIED); + } + + + @Test + public void testUserLifecycleEventOccurred() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = new UserJourneyLogger + .UserJourneySession(10, USER_JOURNEY_USER_START); + + report1.captureLogAndAssert(mUserJourneyLogger, session, 0, + USER_LIFECYCLE_EVENT_START_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED); + } + + @Test + public void testLogUserLifecycleEvent() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_START_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logUserLifecycleEvent(10, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + EVENT_STATE_NONE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, + EVENT_STATE_NONE, ERROR_CODE_UNSPECIFIED, 2); + } + + + @Test + public void testCreateUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(-1, USER_JOURNEY_USER_CREATE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, -1, + USER_LIFECYCLE_EVENT_CREATE_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserCreateJourneyFinish(0, targetUser); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_CREATE_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_CREATE, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testRemoveUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_REMOVE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REMOVE_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_USER_REMOVE); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REMOVE_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_REMOVE, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testStartUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_START, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testStopUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_STOP, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testAbortStopUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + + mUserJourneyLogger.logUserJourneyFinishWithError(-1, targetUser, + USER_JOURNEY_USER_STOP, ERROR_CODE_ABORTED); + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_CANCEL, ERROR_CODE_ABORTED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_STOP, -1, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_ABORTED, 1); + } + + @Test + public void testIncompleteStopUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.finishAndClearIncompleteUserJourney(10, USER_JOURNEY_USER_STOP); + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_ERROR, + ERROR_CODE_INCOMPLETE_OR_TIMEOUT, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_USER_STOP, -1, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN, + -1, // information about user are incomplete + ERROR_CODE_INCOMPLETE_OR_TIMEOUT, 1); + } + + @Test + public void testGrantAdminUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_GRANT_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + USER_JOURNEY_GRANT_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_GRANT_ADMIN, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testNullUserErrorGrantAdminUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + + UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_GRANT_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN, + 0, 10, "", -1); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + UserJourneyLogger.USER_LIFECYCLE_EVENT_GRANT_ADMIN, + EVENT_STATE_ERROR, ERROR_CODE_NULL_USER_INFO, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + USER_JOURNEY_GRANT_ADMIN, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNKNOWN, + -1, ERROR_CODE_NULL_USER_INFO, 1); + } + + @Test + public void testRevokeAdminUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(10, UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REVOKE_ADMIN, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(0, targetUser, + UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 10, + USER_LIFECYCLE_EVENT_REVOKE_ADMIN, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 2); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN, 0, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000400, ERROR_CODE_UNSPECIFIED, 1); + } + + @Test + public void testSwitchFGUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger + .logUserJourneyBegin(11, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(11, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(10, targetUser, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 3); + + report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId, + USER_JOURNEY_USER_START, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 4); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 2); + } + + + @Test + public void testSwitchUIUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger + .logUserJourneyBegin(11, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2); + + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + UserInfo targetUser = new UserInfo(11, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(10, targetUser, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 3); + + report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId, + USER_JOURNEY_USER_START, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 1); + + mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_FINISH, + ERROR_CODE_UNSPECIFIED, 4); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 2); + } + + + @Test + public void testSwitchWithStopUIUserJourney() { + final UserLifecycleEventOccurredCaptor report1 = new UserLifecycleEventOccurredCaptor(); + + // BEGIN USER SWITCH + final UserJourneyLogger.UserJourneySession session = mUserJourneyLogger + .logUserJourneyBegin(11, UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 1); + + // BEGIN USER STOP + final UserJourneyLogger.UserJourneySession session2 = mUserJourneyLogger + .logUserJourneyBegin(10, USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 2); + + // BEGIN USER START + UserJourneyLogger.UserJourneySession session3 = mUserJourneyLogger + .logUserJourneyBegin(11, USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session3.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_BEGIN, ERROR_CODE_UNSPECIFIED, 3); + + + // FINISH USER STOP + final UserLifecycleJourneyReportedCaptor report2 = new UserLifecycleJourneyReportedCaptor(); + final UserInfo targetUser = new UserInfo(10, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(-1, targetUser, + USER_JOURNEY_USER_STOP); + + report1.captureAndAssert(mUserJourneyLogger, session2.mSessionId, 10, + USER_LIFECYCLE_EVENT_STOP_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 4); + + report2.captureAndAssert(mUserJourneyLogger, session2.mSessionId, + USER_JOURNEY_USER_STOP, -1, 10, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + FULL_USER_ADMIN_FLAG, ERROR_CODE_UNSPECIFIED, 1); + + // FINISH USER START + final UserInfo targetUser2 = new UserInfo(11, "test target user", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_FULL); + mUserJourneyLogger.logUserJourneyFinish(10, targetUser2, + USER_JOURNEY_USER_START); + + report1.captureAndAssert(mUserJourneyLogger, session3.mSessionId, 11, + USER_LIFECYCLE_EVENT_START_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 5); + + report2.captureAndAssert(mUserJourneyLogger, session3.mSessionId, + USER_JOURNEY_USER_START, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + FULL_USER_ADMIN_FLAG, ERROR_CODE_UNSPECIFIED, 2); + + + // FINISH USER SWITCH + mUserJourneyLogger.logUserSwitchJourneyFinish(10, targetUser2); + + report1.captureAndAssert(mUserJourneyLogger, session.mSessionId, 11, + UserJourneyLogger.USER_LIFECYCLE_EVENT_SWITCH_USER, + EVENT_STATE_FINISH, ERROR_CODE_UNSPECIFIED, 6); + + report2.captureAndAssert(mUserJourneyLogger, session.mSessionId, + UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI, 10, 11, + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__FULL_SECONDARY, + 0x00000402, ERROR_CODE_UNSPECIFIED, 3); + } + + static class UserLifecycleJourneyReportedCaptor { + ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor<Integer> mJourney = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mOriginalUserId = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mTargetUserId = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mUserType = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mUserFlags = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class); + + public void captureAndAssert(UserJourneyLogger mUserJourneyLogger, + long sessionId, int journey, int originalUserId, + int targetUserId, int userType, int userFlags, int errorCode, int times) { + verify(mUserJourneyLogger, times(times)) + .writeUserLifecycleJourneyReported(mSessionId.capture(), + mJourney.capture(), + mOriginalUserId.capture(), + mTargetUserId.capture(), + mUserType.capture(), + mUserFlags.capture(), + mErrorCode.capture()); + + assertThat(mSessionId.getValue()).isEqualTo(sessionId); + assertThat(mJourney.getValue()).isEqualTo(journey); + assertThat(mOriginalUserId.getValue()).isEqualTo(originalUserId); + assertThat(mTargetUserId.getValue()).isEqualTo(targetUserId); + assertThat(mUserType.getValue()).isEqualTo(userType); + assertThat(mUserFlags.getValue()).isEqualTo(userFlags); + assertThat(mErrorCode.getValue()).isEqualTo(errorCode); + } + + + public void captureLogAndAssert(UserJourneyLogger mUserJourneyLogger, + UserJourneyLogger.UserJourneySession session, int journey, int originalUserId, + int targetUserId, int userType, int userFlags, int errorCode) { + mUserJourneyLogger.logUserLifecycleJourneyReported(session, journey, originalUserId, + targetUserId, userType, userFlags, errorCode); + + captureAndAssert(mUserJourneyLogger, session.mSessionId, journey, originalUserId, + targetUserId, userType, userFlags, errorCode, 1); + } + } + + + static class UserLifecycleEventOccurredCaptor { + ArgumentCaptor<Long> mSessionId = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor<Integer> mTargetUserId = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mEvent = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mStste = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> mErrorCode = ArgumentCaptor.forClass(Integer.class); + + + public void captureAndAssert(UserJourneyLogger mUserJourneyLogger, + long sessionId, int targetUserId, int event, int state, int errorCode, int times) { + verify(mUserJourneyLogger, times(times)) + .writeUserLifecycleEventOccurred(mSessionId.capture(), + mTargetUserId.capture(), + mEvent.capture(), + mStste.capture(), + mErrorCode.capture()); + + assertThat(mSessionId.getValue()).isEqualTo(sessionId); + assertThat(mTargetUserId.getValue()).isEqualTo(targetUserId); + assertThat(mEvent.getValue()).isEqualTo(event); + assertThat(mStste.getValue()).isEqualTo(state); + assertThat(mErrorCode.getValue()).isEqualTo(errorCode); + } + + + public void captureLogAndAssert(UserJourneyLogger mUserJourneyLogger, + UserJourneyLogger.UserJourneySession session, int targetUserId, int event, + int state, int errorCode) { + mUserJourneyLogger.logUserLifecycleEventOccurred(session, targetUserId, event, + state, errorCode); + + captureAndAssert(mUserJourneyLogger, session.mSessionId, targetUserId, event, + state, errorCode, 1); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 52bf244e99fb..ae3ceb1203f8 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; +import static android.os.PowerManager.USER_ACTIVITY_EVENT_BUTTON; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; @@ -41,6 +42,7 @@ import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -112,6 +114,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; @@ -2468,4 +2471,18 @@ public class PowerManagerServiceTest { verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(), anyInt(), any(), any(), same(callback2)); } + + @Test + public void testUserActivity_futureEventsAreIgnored() { + createService(); + startSystem(); + // Starting the system triggers a user activity event, so clear that before calling + // userActivity() directly. + clearInvocations(mNotifierMock); + final long eventTime = mClock.now() + Duration.ofHours(10).toMillis(); + mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, eventTime, + USER_ACTIVITY_EVENT_BUTTON, /* flags= */ 0); + verify(mNotifierMock, never()).onUserActivity(anyInt(), anyInt(), anyInt()); + } + } diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/DeviceStateHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/DeviceStateHandlerTest.java new file mode 100644 index 000000000000..089bd454bfb8 --- /dev/null +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/DeviceStateHandlerTest.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.soundtrigger; + +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY; + +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState; +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState.*; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.SystemClock; + +import androidx.test.filters.FlakyTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.utils.EventLogger; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public final class DeviceStateHandlerTest { + private final long CONFIRM_NO_EVENT_WAIT_MS = 1000; + // A wait substantially less than the duration we delay phone notifications by + private final long PHONE_DELAY_BRIEF_WAIT_MS = + DeviceStateHandler.CALL_INACTIVE_MSG_DELAY_MS / 4; + + private DeviceStateHandler mHandler; + private DeviceStateHandler.DeviceStateListener mDeviceStateCallback; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private SoundTriggerDeviceState mState; + + @GuardedBy("mLock") + private CountDownLatch mLatch; + + private EventLogger mEventLogger; + + @Before + public void setup() { + // Reset the state prior to each test + mEventLogger = new EventLogger(256, "test logger"); + synchronized (mLock) { + mLatch = new CountDownLatch(1); + } + mDeviceStateCallback = + (SoundTriggerDeviceState state) -> { + synchronized (mLock) { + mState = state; + mLatch.countDown(); + } + }; + mHandler = new DeviceStateHandler(Runnable::run, mEventLogger); + mHandler.onPhoneCallStateChanged(false); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + mHandler.registerListener(mDeviceStateCallback); + try { + waitAndAssertState(ENABLE); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void waitAndAssertState(SoundTriggerDeviceState state) throws InterruptedException { + CountDownLatch latch; + synchronized (mLock) { + latch = mLatch; + } + latch.await(); + synchronized (mLock) { + assertThat(mState).isEqualTo(state); + mLatch = new CountDownLatch(1); + } + } + + private void waitToConfirmNoEventReceived() throws InterruptedException { + CountDownLatch latch; + synchronized (mLock) { + latch = mLatch; + } + // Check that we time out + assertThat(latch.await(CONFIRM_NO_EVENT_WAIT_MS, TimeUnit.MILLISECONDS)).isFalse(); + } + + @Test + public void onPowerModeChangedCritical_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitAndAssertState(ENABLE); + } + + @Test + public void onPowerModeChangedDisabled_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitAndAssertState(ENABLE); + } + + @Test + public void onPowerModeChangedMultiple_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + } + + @Test + public void onPowerModeSameState_noStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitAndAssertState(ENABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitToConfirmNoEventReceived(); + } + + @Test + public void onPhoneCall_receiveStateChange() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(ENABLE); + } + + @Test + public void onPhoneCall_receiveStateChangeIsDelayed() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + long beforeTime = SystemClock.uptimeMillis(); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(ENABLE); + long afterTime = SystemClock.uptimeMillis(); + assertThat(afterTime - beforeTime).isAtLeast(DeviceStateHandler.CALL_INACTIVE_MSG_DELAY_MS); + } + + @Test + public void onPhoneCallEnterExitEnter_receiveNoStateChange() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + SystemClock.sleep(PHONE_DELAY_BRIEF_WAIT_MS); + mHandler.onPhoneCallStateChanged(true); + waitToConfirmNoEventReceived(); + } + + @Test + public void onBatteryCallbackDuringPhoneWait_receiveStateChangeDelayed() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + SystemClock.sleep(PHONE_DELAY_BRIEF_WAIT_MS); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + // Ensure we don't get an ENABLE event after + waitToConfirmNoEventReceived(); + } + + @Test + public void onBatteryChangeWhenInPhoneCall_receiveNoStateChange() throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitToConfirmNoEventReceived(); + } + + @Test + public void whenBatteryCriticalChangeDuringCallAfterPhoneCall_receiveCriticalStateChange() + throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitToConfirmNoEventReceived(); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(CRITICAL); + } + + @Test + public void whenBatteryDisableDuringCallAfterPhoneCallBatteryEnable_receiveStateChange() + throws Exception { + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitToConfirmNoEventReceived(); + mHandler.onPhoneCallStateChanged(false); + waitToConfirmNoEventReceived(); + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + } + + @Test + public void whenPhoneCallDuringBatteryDisable_receiveNoStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(true); + waitToConfirmNoEventReceived(); + mHandler.onPhoneCallStateChanged(false); + waitToConfirmNoEventReceived(); + } + + @Test + public void whenPhoneCallDuringBatteryCritical_receiveStateChange() throws Exception { + mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); + waitAndAssertState(CRITICAL); + mHandler.onPhoneCallStateChanged(true); + waitAndAssertState(DISABLE); + mHandler.onPhoneCallStateChanged(false); + waitAndAssertState(CRITICAL); + } + + // This test could be flaky, but we want to verify that we only delay notification if + // we are exiting a call, NOT if we are entering a call. + @FlakyTest + @Test + public void whenPhoneCallReceived_receiveStateChangeFast() throws Exception { + mHandler.onPhoneCallStateChanged(true); + CountDownLatch latch; + synchronized (mLock) { + latch = mLatch; + } + assertThat(latch.await(PHONE_DELAY_BRIEF_WAIT_MS, TimeUnit.MILLISECONDS)).isTrue(); + synchronized (mLock) { + assertThat(mState).isEqualTo(DISABLE); + } + } +} diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java index 2d0755d00ba8..3ac9a2758e08 100644 --- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java @@ -16,6 +16,8 @@ package com.android.server.soundtrigger_middleware; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -34,12 +36,12 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.Status; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.HwParcel; import android.os.IBinder; import android.os.IHwBinder; @@ -617,13 +619,16 @@ public class SoundHw2CompatTest { final int handle = 85; final int status = android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT; - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); hwCallback.recognitionCallback(TestUtil.createRecognitionEvent_2_0(handle, status), 99); mCanonical.flushCallbacks(); verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture()); - TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED, + RecognitionEventSys lastEvent = eventCaptor.getValue(); + assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0); + TestUtil.validateRecognitionEvent(lastEvent.recognitionEvent, + RecognitionStatus.ABORTED, false); } @@ -631,14 +636,16 @@ public class SoundHw2CompatTest { final int handle = 92; final int status = android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS; - ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - PhraseRecognitionEvent.class); + ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEventSys.class); hwCallback.phraseRecognitionCallback( TestUtil.createPhraseRecognitionEvent_2_0(handle, status), 99); mCanonical.flushCallbacks(); verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture()); - TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(), + PhraseRecognitionEventSys lastEvent = eventCaptor.getValue(); + assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0); + TestUtil.validatePhraseRecognitionEvent(lastEvent.phraseRecognitionEvent, RecognitionStatus.SUCCESS, false); } verifyNoMoreInteractions(canonicalCallback); @@ -652,28 +659,34 @@ public class SoundHw2CompatTest { final int handle = 85; final int status = android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT; - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status), 99); mCanonical.flushCallbacks(); verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture()); - TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED, + RecognitionEventSys lastEvent = eventCaptor.getValue(); + assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0); + TestUtil.validateRecognitionEvent(lastEvent.recognitionEvent, + RecognitionStatus.ABORTED, false); } { final int handle = 87; final int status = 3; // FORCED; - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status), 99); mCanonical.flushCallbacks(); verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture()); - TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED, + RecognitionEventSys lastEvent = eventCaptor.getValue(); + assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0); + TestUtil.validateRecognitionEvent(lastEvent.recognitionEvent, + RecognitionStatus.FORCED, true); } @@ -681,28 +694,32 @@ public class SoundHw2CompatTest { final int handle = 92; final int status = android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS; - ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - PhraseRecognitionEvent.class); + ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEventSys.class); hwCallback.phraseRecognitionCallback_2_1( TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99); mCanonical.flushCallbacks(); verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture()); - TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(), + PhraseRecognitionEventSys lastEvent = eventCaptor.getValue(); + assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0); + TestUtil.validatePhraseRecognitionEvent(lastEvent.phraseRecognitionEvent, RecognitionStatus.SUCCESS, false); } { final int handle = 102; final int status = 3; // FORCED; - ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - PhraseRecognitionEvent.class); + ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEventSys.class); hwCallback.phraseRecognitionCallback_2_1( TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99); mCanonical.flushCallbacks(); verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture()); - TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(), + PhraseRecognitionEventSys lastEvent = eventCaptor.getValue(); + assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0); + TestUtil.validatePhraseRecognitionEvent(lastEvent.phraseRecognitionEvent, RecognitionStatus.FORCED, true); } verifyNoMoreInteractions(canonicalCallback); diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java index 61989252d04d..9a59ede20e23 100644 --- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java @@ -30,8 +30,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; +import android.media.soundtrigger_middleware.RecognitionEventSys; import androidx.annotation.NonNull; @@ -68,13 +68,14 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { mNotifier.setActive(true); verify(mUnderlying).stopRecognition(handle); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); Thread.sleep(50); verify(callback).recognitionCallback(eq(handle), eventCaptor.capture()); - RecognitionEvent event = eventCaptor.getValue(); - assertEquals(event.status, RecognitionStatus.ABORTED); - assertFalse(event.recognitionStillActive); + RecognitionEventSys event = eventCaptor.getValue(); + assertEquals(event.halEventReceivedMillis, -1); + assertEquals(event.recognitionEvent.status, RecognitionStatus.ABORTED); + assertFalse(event.recognitionEvent.recognitionStillActive); verifyZeroInteractions(mGlobalCallback); clearInvocations(callback, mUnderlying); @@ -116,8 +117,11 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { mNotifier.setActive(true); verify(mUnderlying, times(1)).stopRecognition(handle); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); mHandler.stopRecognition(handle); - verify(callback, times(1)).recognitionCallback(eq(handle), any()); + verify(callback).recognitionCallback(eq(handle), eventCaptor.capture()); + assertEquals(eventCaptor.getValue().halEventReceivedMillis, -1); } @Test(timeout = 200) @@ -133,19 +137,21 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); doAnswer(invocation -> { - RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.ABORTED, + RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); + recognitionEventSys.recognitionEvent = TestUtil.createRecognitionEvent( + RecognitionStatus.ABORTED, false); + recognitionEventSys.halEventReceivedMillis = 12345; // Call the callback from a different thread to detect deadlocks by preventing recursive // locking from working. - runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + runOnSeparateThread( + () -> modelCallback.recognitionCallback(handle, recognitionEventSys)); return null; }).when(mUnderlying).stopRecognition(handle); mHandler.stopRecognition(handle); verify(mUnderlying, times(1)).stopRecognition(handle); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); - verify(callback, atMost(1)).recognitionCallback(eq(handle), eventCaptor.capture()); + verify(callback, atMost(1)).recognitionCallback(eq(handle), any(RecognitionEventSys.class)); } @Test(timeout = 200) @@ -162,11 +168,15 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { doAnswer(invocation -> { // The stop request causes a callback to be flushed. - RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.FORCED, + RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); + recognitionEventSys.recognitionEvent = TestUtil.createRecognitionEvent( + RecognitionStatus.FORCED, true); + recognitionEventSys.halEventReceivedMillis = 12345; // Call the callback from a different thread to detect deadlocks by preventing recursive // locking from working. - runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + runOnSeparateThread( + () -> modelCallback.recognitionCallback(handle, recognitionEventSys)); // While the HAL is processing the stop request, capture state becomes active. new Thread(() -> mNotifier.setActive(true)).start(); Thread.sleep(50); @@ -194,11 +204,15 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { doAnswer(invocation -> { // The stop request causes a callback to be flushed. - RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.FORCED, + RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); + recognitionEventSys.recognitionEvent = TestUtil.createRecognitionEvent( + RecognitionStatus.FORCED, true); + recognitionEventSys.halEventReceivedMillis = 12345; // Call the callback from a different thread to detect deadlocks by preventing recursive // locking from working. - runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + runOnSeparateThread( + () -> modelCallback.recognitionCallback(handle, recognitionEventSys)); // While the HAL is processing the stop request, client requests stop. new Thread(() -> mHandler.stopRecognition(handle)).start(); Thread.sleep(50); @@ -223,23 +237,22 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); doAnswer(invocation -> { - RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.SUCCESS, + RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); + recognitionEventSys.recognitionEvent = TestUtil.createRecognitionEvent( + RecognitionStatus.SUCCESS, false); + recognitionEventSys.halEventReceivedMillis = 12345; // Call the callback from a different thread to detect deadlocks by preventing recursive // locking from working. - runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + runOnSeparateThread( + () -> modelCallback.recognitionCallback(handle, recognitionEventSys)); return null; }).when(mUnderlying).stopRecognition(handle); mNotifier.setActive(true); verify(mUnderlying, times(1)).stopRecognition(handle); Thread.sleep(50); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); - verify(callback, atMost(2)).recognitionCallback(eq(handle), eventCaptor.capture()); - RecognitionEvent lastEvent = eventCaptor.getValue(); - assertEquals(lastEvent.status, RecognitionStatus.ABORTED); - assertFalse(lastEvent.recognitionStillActive); + verify(callback, atMost(2)).recognitionCallback(eq(handle), any()); } @@ -256,11 +269,15 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { verify(mUnderlying).startRecognition(eq(handle), eq(101), eq(102), any()); doAnswer(invocation -> { - RecognitionEvent event = TestUtil.createRecognitionEvent(RecognitionStatus.FORCED, + RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); + recognitionEventSys.recognitionEvent = TestUtil.createRecognitionEvent( + RecognitionStatus.FORCED, true); + recognitionEventSys.halEventReceivedMillis = 12345; // Call the callback from a different thread to detect deadlocks by preventing recursive // locking from working. - runOnSeparateThread(() -> modelCallback.recognitionCallback(handle, event)); + runOnSeparateThread( + () -> modelCallback.recognitionCallback(handle, recognitionEventSys)); return null; }).when(mUnderlying).stopRecognition(handle); @@ -268,12 +285,7 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest { verify(mUnderlying, times(1)).stopRecognition(handle); Thread.sleep(50); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); - verify(callback, atMost(2)).recognitionCallback(eq(handle), eventCaptor.capture()); - RecognitionEvent lastEvent = eventCaptor.getValue(); - assertEquals(lastEvent.status, RecognitionStatus.ABORTED); - assertFalse(lastEvent.recognitionStillActive); + verify(callback, atMost(2)).recognitionCallback(eq(handle), any()); } private static void runOnSeparateThread(Runnable runnable) { diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index 3bebc94fe0ed..5a2451f3b17e 100644 --- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -30,18 +30,19 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.ElapsedRealtimeLong; import android.media.soundtrigger.ModelParameter; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.Status; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.os.RemoteException; import android.util.Pair; @@ -224,10 +225,12 @@ public class SoundTriggerMiddlewareImplTest { // Stop the recognition. stopRecognition(module, handle, hwHandle); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101)); - assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status); + RecognitionEventSys lastEvent = eventCaptor.getValue(); + assertEquals(-1, lastEvent.halEventReceivedMillis); + assertEquals(RecognitionStatus.ABORTED, lastEvent.recognitionEvent.status); // Unload the model. unloadModel(module, handle, hwHandle); @@ -273,10 +276,12 @@ public class SoundTriggerMiddlewareImplTest { // Stop the recognition. stopRecognition(module, handle, hwHandle); - ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - PhraseRecognitionEvent.class); + ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEventSys.class); verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101)); - assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status); + PhraseRecognitionEventSys lastEvent = eventCaptor.getValue(); + assertEquals(-1, lastEvent.halEventReceivedMillis); + assertEquals(RecognitionStatus.ABORTED, lastEvent.phraseRecognitionEvent.common.status); // Unload the model. unloadModel(module, handle, hwHandle); @@ -299,11 +304,11 @@ public class SoundTriggerMiddlewareImplTest { { // Signal a capture from the driver (with "still active"). - RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle, - RecognitionStatus.SUCCESS, true); + RecognitionEventSys event = hwCallback.sendRecognitionEvent(hwHandle, + RecognitionStatus.SUCCESS, true, 12345); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101)); // Validate the event. @@ -312,11 +317,11 @@ public class SoundTriggerMiddlewareImplTest { { // Signal a capture from the driver (without "still active"). - RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle, - RecognitionStatus.SUCCESS, false); + RecognitionEventSys event = hwCallback.sendRecognitionEvent(hwHandle, + RecognitionStatus.SUCCESS, false, 12345); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); verify(callback, times(2)).onRecognition(eq(handle), eventCaptor.capture(), eq(101)); // Validate the event. @@ -343,11 +348,11 @@ public class SoundTriggerMiddlewareImplTest { startRecognition(module, handle, hwHandle); // Signal a capture from the driver. - PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle, - RecognitionStatus.SUCCESS, false); + PhraseRecognitionEventSys event = hwCallback.sendPhraseRecognitionEvent(hwHandle, + RecognitionStatus.SUCCESS, false, 12345); - ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - PhraseRecognitionEvent.class); + ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEventSys.class); verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101)); // Validate the event. @@ -377,11 +382,11 @@ public class SoundTriggerMiddlewareImplTest { verify(mHalDriver).forceRecognitionEvent(hwHandle); // Signal a capture from the driver. - RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle, - RecognitionStatus.FORCED, true); + RecognitionEventSys event = hwCallback.sendRecognitionEvent(hwHandle, + RecognitionStatus.FORCED, true, 12345); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101)); // Validate the event. @@ -445,11 +450,11 @@ public class SoundTriggerMiddlewareImplTest { verify(mHalDriver).forceRecognitionEvent(hwHandle); // Signal a capture from the driver. - PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle, - RecognitionStatus.FORCED, true); + PhraseRecognitionEventSys event = hwCallback.sendPhraseRecognitionEvent(hwHandle, + RecognitionStatus.FORCED, true, 12345); - ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - PhraseRecognitionEvent.class); + ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEventSys.class); verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101)); // Validate the event. @@ -510,14 +515,16 @@ public class SoundTriggerMiddlewareImplTest { startRecognition(module, handle, hwHandle); // Abort. - hwCallback.sendRecognitionEvent(hwHandle, RecognitionStatus.ABORTED, false); + hwCallback.sendRecognitionEvent(hwHandle, RecognitionStatus.ABORTED, false, 12345); - ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - RecognitionEvent.class); + ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + RecognitionEventSys.class); verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101)); // Validate the event. - assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status); + RecognitionEventSys lastEvent = eventCaptor.getValue(); + assertEquals(12345, lastEvent.halEventReceivedMillis); + assertEquals(RecognitionStatus.ABORTED, lastEvent.recognitionEvent.status); // Unload the model. unloadModel(module, handle, hwHandle); @@ -540,14 +547,16 @@ public class SoundTriggerMiddlewareImplTest { startRecognition(module, handle, hwHandle); // Abort. - hwCallback.sendPhraseRecognitionEvent(hwHandle, RecognitionStatus.ABORTED, false); + hwCallback.sendPhraseRecognitionEvent(hwHandle, RecognitionStatus.ABORTED, false, 12345); - ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( - PhraseRecognitionEvent.class); + ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEventSys.class); verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101)); // Validate the event. - assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status); + PhraseRecognitionEventSys lastEvent = eventCaptor.getValue(); + assertEquals(12345, lastEvent.halEventReceivedMillis); + assertEquals(RecognitionStatus.ABORTED, lastEvent.phraseRecognitionEvent.common.status); // Unload the model. unloadModel(module, handle, hwHandle); @@ -630,18 +639,24 @@ public class SoundTriggerMiddlewareImplTest { mCallback = callback; } - private RecognitionEvent sendRecognitionEvent(int hwHandle, @RecognitionStatus int status, - boolean recognitionStillActive) { - RecognitionEvent event = TestUtil.createRecognitionEvent(status, + private RecognitionEventSys sendRecognitionEvent(int hwHandle, + @RecognitionStatus int status, + boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) { + RecognitionEventSys event = new RecognitionEventSys(); + event.recognitionEvent = TestUtil.createRecognitionEvent(status, recognitionStillActive); + event.halEventReceivedMillis = halEventReceivedMillis; mCallback.recognitionCallback(hwHandle, event); return event; } - private PhraseRecognitionEvent sendPhraseRecognitionEvent(int hwHandle, - @RecognitionStatus int status, boolean recognitionStillActive) { - PhraseRecognitionEvent event = TestUtil.createPhraseRecognitionEvent(status, + private PhraseRecognitionEventSys sendPhraseRecognitionEvent(int hwHandle, + @RecognitionStatus int status, boolean recognitionStillActive, + @ElapsedRealtimeLong long halEventReceivedMillis) { + PhraseRecognitionEventSys event = new PhraseRecognitionEventSys(); + event.phraseRecognitionEvent = TestUtil.createPhraseRecognitionEvent(status, recognitionStillActive); + event.halEventReceivedMillis = halEventReceivedMillis; mCallback.phraseRecognitionCallback(hwHandle, event); return event; } diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java index f8a068c7f121..cc357d76cb4a 100644 --- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java @@ -32,7 +32,7 @@ import android.media.soundtrigger.PhraseRecognitionExtra; import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger_middleware.ISoundTriggerCallback; -import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; import android.os.BatteryStatsInternal; import android.os.Process; import android.os.RemoteException; @@ -51,8 +51,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.Optional; - @RunWith(JUnit4.class) public class SoundTriggerMiddlewareLoggingLatencyTest { @@ -63,8 +61,6 @@ public class SoundTriggerMiddlewareLoggingLatencyTest { private ISoundTriggerMiddlewareInternal mDelegateMiddleware; @Mock private ISoundTriggerCallback mISoundTriggerCallback; - @Mock - private ISoundTriggerModule mSoundTriggerModule; private SoundTriggerMiddlewareLogging mSoundTriggerMiddlewareLogging; @Before @@ -109,7 +105,7 @@ public class SoundTriggerMiddlewareLoggingLatencyTest { verify(mDelegateMiddleware).attach(anyInt(), soundTriggerCallbackCaptor.capture()); triggerPhraseRecognitionEvent(soundTriggerCallbackCaptor.getValue(), - RecognitionStatus.SUCCESS, Optional.of(100) /* keyphraseId */); + RecognitionStatus.SUCCESS, 100 /* keyphraseId */); assertThat(mLatencyTracker.getActiveActionStartTime( ACTION_SHOW_VOICE_INTERACTION)).isGreaterThan(-1); @@ -124,11 +120,11 @@ public class SoundTriggerMiddlewareLoggingLatencyTest { verify(mDelegateMiddleware).attach(anyInt(), soundTriggerCallbackCaptor.capture()); triggerPhraseRecognitionEvent(soundTriggerCallbackCaptor.getValue(), - RecognitionStatus.SUCCESS, Optional.of(100) /* keyphraseId */); + RecognitionStatus.SUCCESS, 100 /* keyphraseId */); long firstTriggerSessionStartTime = mLatencyTracker.getActiveActionStartTime( ACTION_SHOW_VOICE_INTERACTION); triggerPhraseRecognitionEvent(soundTriggerCallbackCaptor.getValue(), - RecognitionStatus.SUCCESS, Optional.of(100) /* keyphraseId */); + RecognitionStatus.SUCCESS, 100 /* keyphraseId */); assertThat(mLatencyTracker.getActiveActionStartTime( ACTION_SHOW_VOICE_INTERACTION)).isGreaterThan(-1); assertThat(mLatencyTracker.getActiveActionStartTime( @@ -145,7 +141,7 @@ public class SoundTriggerMiddlewareLoggingLatencyTest { verify(mDelegateMiddleware).attach(anyInt(), soundTriggerCallbackCaptor.capture()); triggerPhraseRecognitionEvent(soundTriggerCallbackCaptor.getValue(), - RecognitionStatus.ABORTED, Optional.of(100) /* keyphraseId */); + RecognitionStatus.ABORTED, 100 /* keyphraseId */); assertThat( mLatencyTracker.getActiveActionStartTime(ACTION_SHOW_VOICE_INTERACTION)).isEqualTo( @@ -162,7 +158,7 @@ public class SoundTriggerMiddlewareLoggingLatencyTest { verify(mDelegateMiddleware).attach(anyInt(), soundTriggerCallbackCaptor.capture()); triggerPhraseRecognitionEvent(soundTriggerCallbackCaptor.getValue(), - RecognitionStatus.SUCCESS, Optional.empty() /* keyphraseId */); + RecognitionStatus.SUCCESS); assertThat( mLatencyTracker.getActiveActionStartTime(ACTION_SHOW_VOICE_INTERACTION)).isEqualTo( @@ -170,19 +166,27 @@ public class SoundTriggerMiddlewareLoggingLatencyTest { } private void triggerPhraseRecognitionEvent(ISoundTriggerCallback callback, - @RecognitionStatus int triggerEventStatus, Optional<Integer> optionalKeyphraseId) - throws RemoteException { + @RecognitionStatus int triggerEventStatus) throws RemoteException { + triggerPhraseRecognitionEvent(callback, triggerEventStatus, -1 /* keyphraseId */); + } + + private void triggerPhraseRecognitionEvent(ISoundTriggerCallback callback, + @RecognitionStatus int triggerEventStatus, int keyphraseId) throws RemoteException { // trigger a phrase recognition to start a latency tracker session PhraseRecognitionEvent successEventWithKeyphraseId = new PhraseRecognitionEvent(); successEventWithKeyphraseId.common = new RecognitionEvent(); successEventWithKeyphraseId.common.status = triggerEventStatus; - if (optionalKeyphraseId.isPresent()) { + if (keyphraseId > 0) { PhraseRecognitionExtra recognitionExtra = new PhraseRecognitionExtra(); - recognitionExtra.id = optionalKeyphraseId.get(); + recognitionExtra.id = keyphraseId; successEventWithKeyphraseId.phraseExtras = new PhraseRecognitionExtra[]{recognitionExtra}; } - callback.onPhraseRecognition(0 /* modelHandle */, successEventWithKeyphraseId, + PhraseRecognitionEventSys phraseRecognitionEventSys = new PhraseRecognitionEventSys(); + phraseRecognitionEventSys.phraseRecognitionEvent = successEventWithKeyphraseId; + phraseRecognitionEventSys.halEventReceivedMillis = 12345; + callback.onPhraseRecognition(0 /* modelHandle */, phraseRecognitionEventSys, 0 /* captureSession */); } + } diff --git a/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java b/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java new file mode 100644 index 000000000000..cc8dab99c818 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.WindowManager.TRANSIT_CHANGE; + +import static com.android.server.wm.DeviceStateController.DeviceState.FOLDED; +import static com.android.server.wm.DeviceStateController.DeviceState.HALF_FOLDED; +import static com.android.server.wm.DeviceStateController.DeviceState.OPEN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.window.TransitionRequestInfo.DisplayChange; + +import static com.android.internal.R.bool.config_unfoldTransitionEnabled; +import static com.android.server.wm.DeviceStateController.DeviceState.REAR; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for the {@link WindowToken} class. + * + * Build/Install/Run: + * atest WmTests:PhysicalDisplaySwitchTransitionLauncherTest + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase { + + @Mock + DisplayContent mDisplayContent; + @Mock + Context mContext; + @Mock + Resources mResources; + @Mock + ActivityTaskManagerService mActivityTaskManagerService; + @Mock + TransitionController mTransitionController; + + private PhysicalDisplaySwitchTransitionLauncher mTarget; + private float mOriginalAnimationScale; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getResources()).thenReturn(mResources); + mTarget = new PhysicalDisplaySwitchTransitionLauncher(mDisplayContent, + mActivityTaskManagerService, mContext, mTransitionController); + mOriginalAnimationScale = ValueAnimator.getDurationScale(); + } + + @After + public void after() { + ValueAnimator.setDurationScale(mOriginalAnimationScale); + } + + @Test + public void testDisplaySwitchAfterUnfoldToOpen_animationsEnabled_requestsTransition() { + givenAllAnimationsEnabled(); + mTarget.foldStateChanged(FOLDED); + + mTarget.foldStateChanged(OPEN); + mTarget.requestDisplaySwitchTransitionIfNeeded( + /* displayId= */ 123, + /* oldDisplayWidth= */ 100, + /* oldDisplayHeight= */ 150, + /* newDisplayWidth= */ 200, + /* newDisplayHeight= */ 250 + ); + + ArgumentCaptor<DisplayChange> displayChangeArgumentCaptor = + ArgumentCaptor.forClass(DisplayChange.class); + verify(mTransitionController).requestTransitionIfNeeded(eq(TRANSIT_CHANGE), /* flags= */ + eq(0), eq(mDisplayContent), eq(mDisplayContent), /* remoteTransition= */ isNull(), + displayChangeArgumentCaptor.capture()); + assertThat(displayChangeArgumentCaptor.getValue().getDisplayId()).isEqualTo(123); + assertThat(displayChangeArgumentCaptor.getValue().getStartAbsBounds()).isEqualTo( + new Rect(0, 0, 100, 150)); + assertThat(displayChangeArgumentCaptor.getValue().getEndAbsBounds()).isEqualTo( + new Rect(0, 0, 200, 250)); + } + + @Test + public void testDisplaySwitchAfterFolding_animationEnabled_doesNotRequestTransition() { + givenAllAnimationsEnabled(); + mTarget.foldStateChanged(OPEN); + + mTarget.foldStateChanged(FOLDED); + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + @Test + public void testDisplaySwitchAfterUnfoldingToHalf_animationEnabled_requestsTransition() { + givenAllAnimationsEnabled(); + mTarget.foldStateChanged(FOLDED); + + mTarget.foldStateChanged(HALF_FOLDED); + requestDisplaySwitch(); + + assertTransitionRequested(); + } + + @Test + public void testDisplaySwitchSecondTimeAfterUnfolding_animationEnabled_noTransition() { + givenAllAnimationsEnabled(); + mTarget.foldStateChanged(FOLDED); + mTarget.foldStateChanged(OPEN); + requestDisplaySwitch(); + clearInvocations(mTransitionController); + + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + + @Test + public void testDisplaySwitchAfterGoingToRearAndBack_animationEnabled_noTransition() { + givenAllAnimationsEnabled(); + mTarget.foldStateChanged(OPEN); + + mTarget.foldStateChanged(REAR); + mTarget.foldStateChanged(OPEN); + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + @Test + public void testDisplaySwitchAfterUnfoldingAndFolding_animationEnabled_noTransition() { + givenAllAnimationsEnabled(); + mTarget.foldStateChanged(FOLDED); + mTarget.foldStateChanged(OPEN); + // No request display switch event (simulate very fast fold after unfold, even before + // the displays switched) + mTarget.foldStateChanged(FOLDED); + + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + @Test + public void testDisplaySwitch_whenShellTransitionsNotEnabled_noTransition() { + givenAllAnimationsEnabled(); + givenShellTransitionsEnabled(false); + mTarget.foldStateChanged(FOLDED); + + mTarget.foldStateChanged(OPEN); + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + @Test + public void testDisplaySwitch_whenAnimationsDisabled_noTransition() { + givenAllAnimationsEnabled(); + givenAnimationsEnabled(false); + mTarget.foldStateChanged(FOLDED); + + mTarget.foldStateChanged(OPEN); + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + @Test + public void testDisplaySwitch_whenUnfoldAnimationDisabled_noTransition() { + givenAllAnimationsEnabled(); + givenUnfoldTransitionEnabled(false); + mTarget.foldStateChanged(FOLDED); + + mTarget.foldStateChanged(OPEN); + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + @Test + public void testDisplaySwitch_whenNoContentInDisplayContent_noTransition() { + givenAllAnimationsEnabled(); + givenDisplayContentHasContent(false); + mTarget.foldStateChanged(FOLDED); + + mTarget.foldStateChanged(OPEN); + requestDisplaySwitch(); + + assertTransitionNotRequested(); + } + + private void assertTransitionRequested() { + verify(mTransitionController).requestTransitionIfNeeded(anyInt(), anyInt(), any(), any(), + any(), any()); + } + + private void assertTransitionNotRequested() { + verify(mTransitionController, never()).requestTransitionIfNeeded(anyInt(), anyInt(), any(), + any(), any(), any()); + } + + private void requestDisplaySwitch() { + mTarget.requestDisplaySwitchTransitionIfNeeded( + /* displayId= */ 123, + /* oldDisplayWidth= */ 100, + /* oldDisplayHeight= */ 150, + /* newDisplayWidth= */ 200, + /* newDisplayHeight= */ 250 + ); + } + + private void givenAllAnimationsEnabled() { + givenAnimationsEnabled(true); + givenUnfoldTransitionEnabled(true); + givenShellTransitionsEnabled(true); + givenDisplayContentHasContent(true); + } + + private void givenUnfoldTransitionEnabled(boolean enabled) { + when(mResources.getBoolean(config_unfoldTransitionEnabled)).thenReturn(enabled); + } + + private void givenAnimationsEnabled(boolean enabled) { + ValueAnimator.setDurationScale(enabled ? 1.0f : 0.0f); + } + + private void givenShellTransitionsEnabled(boolean enabled) { + when(mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled); + } + + private void givenDisplayContentHasContent(boolean hasContent) { + when(mDisplayContent.getLastHasContent()).thenReturn(hasContent); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java index c8fc6b892bc8..d84620b4444a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java @@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest; import com.android.server.testutils.StubTransaction; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -52,6 +53,11 @@ public class SurfaceSyncGroupTest { SurfaceSyncGroup.setTransactionFactory(StubTransaction::new); } + @After + public void tearDown() { + SurfaceSyncGroup.setTransactionFactory(SurfaceControl.Transaction::new); + } + @Test public void testSyncOne() throws InterruptedException { final CountDownLatch finishedLatch = new CountDownLatch(1); diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/DeviceStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/DeviceStateHandler.java new file mode 100644 index 000000000000..66054494c277 --- /dev/null +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/DeviceStateHandler.java @@ -0,0 +1,279 @@ +/** + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.soundtrigger; + +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED; +import static android.os.PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.utils.EventLogger; + +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Manages device state events which require pausing SoundTrigger recognition + * + * @hide + */ +public class DeviceStateHandler implements PhoneCallStateHandler.Callback { + + public static final long CALL_INACTIVE_MSG_DELAY_MS = 1000; + + public interface DeviceStateListener { + void onSoundTriggerDeviceStateUpdate(SoundTriggerDeviceState state); + } + + public enum SoundTriggerDeviceState { + DISABLE, // The device state requires all SoundTrigger sessions are disabled + CRITICAL, // The device state requires all non-critical SoundTrigger sessions are disabled + ENABLE // The device state permits all SoundTrigger sessions + } + + private final Object mLock = new Object(); + + private final EventLogger mEventLogger; + + @GuardedBy("mLock") + SoundTriggerDeviceState mSoundTriggerDeviceState = SoundTriggerDeviceState.ENABLE; + + // Individual components of the SoundTriggerDeviceState + @GuardedBy("mLock") + private int mSoundTriggerPowerSaveMode = SOUND_TRIGGER_MODE_ALL_ENABLED; + + @GuardedBy("mLock") + private boolean mIsPhoneCallOngoing = false; + + // There can only be one pending notify at any given time. + // If any phone state change comes in between, we will cancel the previous pending + // task. + @GuardedBy("mLock") + private NotificationTask mPhoneStateChangePendingNotify = null; + + private Set<DeviceStateListener> mCallbackSet = ConcurrentHashMap.newKeySet(4); + + private final Executor mDelayedNotificationExecutor = Executors.newSingleThreadExecutor(); + + private final Executor mCallbackExecutor; + + public void onPowerModeChanged(int soundTriggerPowerSaveMode) { + mEventLogger.enqueue(new SoundTriggerPowerEvent(soundTriggerPowerSaveMode)); + synchronized (mLock) { + if (soundTriggerPowerSaveMode == mSoundTriggerPowerSaveMode) { + // No state change, nothing to do + return; + } + mSoundTriggerPowerSaveMode = soundTriggerPowerSaveMode; + evaluateStateChange(); + } + } + + @Override + public void onPhoneCallStateChanged(boolean isInPhoneCall) { + mEventLogger.enqueue(new PhoneCallEvent(isInPhoneCall)); + synchronized (mLock) { + if (mIsPhoneCallOngoing == isInPhoneCall) { + // no change, nothing to do + return; + } + // Clear any pending notification + if (mPhoneStateChangePendingNotify != null) { + mPhoneStateChangePendingNotify.cancel(); + mPhoneStateChangePendingNotify = null; + } + mIsPhoneCallOngoing = isInPhoneCall; + if (!mIsPhoneCallOngoing) { + // State has changed from call to no call, delay notification + mPhoneStateChangePendingNotify = new NotificationTask( + new Runnable() { + @Override + public void run() { + synchronized (mLock) { + if (mPhoneStateChangePendingNotify != null && + mPhoneStateChangePendingNotify.runnableEquals(this)) { + + mPhoneStateChangePendingNotify = null; + evaluateStateChange(); + } + } + } + }, + CALL_INACTIVE_MSG_DELAY_MS); + mDelayedNotificationExecutor.execute(mPhoneStateChangePendingNotify); + } else { + evaluateStateChange(); + } + } + } + + /** Note, we expect initial callbacks immediately following construction */ + public DeviceStateHandler(Executor callbackExecutor, EventLogger eventLogger) { + mCallbackExecutor = Objects.requireNonNull(callbackExecutor); + mEventLogger = Objects.requireNonNull(eventLogger); + } + + public SoundTriggerDeviceState getDeviceState() { + synchronized (mLock) { + return mSoundTriggerDeviceState; + } + } + + public void registerListener(DeviceStateListener callback) { + final var state = getDeviceState(); + mCallbackExecutor.execute( + () -> callback.onSoundTriggerDeviceStateUpdate(state)); + mCallbackSet.add(callback); + } + + public void unregisterListener(DeviceStateListener callback) { + mCallbackSet.remove(callback); + } + + void dump(PrintWriter pw) { + synchronized (mLock) { + pw.println("DeviceState: " + mSoundTriggerDeviceState.name()); + pw.println("PhoneState: " + mIsPhoneCallOngoing); + pw.println("PowerSaveMode: " + mSoundTriggerPowerSaveMode); + } + } + + @GuardedBy("mLock") + private void evaluateStateChange() { + // We should wait until any pending delays are complete to update. + // We will eventually get called by the notification task, or something which + // cancels it. + // Additionally, if there isn't a state change, there is nothing to update. + SoundTriggerDeviceState newState = computeState(); + if (mPhoneStateChangePendingNotify != null || mSoundTriggerDeviceState == newState) { + return; + } + + mSoundTriggerDeviceState = newState; + mEventLogger.enqueue(new DeviceStateEvent(mSoundTriggerDeviceState)); + final var state = mSoundTriggerDeviceState; + for (var callback : mCallbackSet) { + mCallbackExecutor.execute( + () -> callback.onSoundTriggerDeviceStateUpdate(state)); + } + } + + @GuardedBy("mLock") + private SoundTriggerDeviceState computeState() { + if (mIsPhoneCallOngoing) { + return SoundTriggerDeviceState.DISABLE; + } + return switch (mSoundTriggerPowerSaveMode) { + case SOUND_TRIGGER_MODE_ALL_ENABLED -> SoundTriggerDeviceState.ENABLE; + case SOUND_TRIGGER_MODE_CRITICAL_ONLY -> SoundTriggerDeviceState.CRITICAL; + case SOUND_TRIGGER_MODE_ALL_DISABLED -> SoundTriggerDeviceState.DISABLE; + default -> throw new IllegalStateException( + "Received unexpected power state code" + mSoundTriggerPowerSaveMode); + }; + } + + /** + * One-shot, cancellable task which runs after a delay. Run must only be called once, from a + * single thread. Cancel can be called from any other thread. + */ + private static class NotificationTask implements Runnable { + private final Runnable mRunnable; + private final long mWaitInMillis; + + private final CountDownLatch mCancelLatch = new CountDownLatch(1); + + NotificationTask(Runnable r, long waitInMillis) { + mRunnable = r; + mWaitInMillis = waitInMillis; + } + + void cancel() { + mCancelLatch.countDown(); + } + + // Used for determining task equality. + boolean runnableEquals(Runnable runnable) { + return mRunnable == runnable; + } + + public void run() { + try { + if (!mCancelLatch.await(mWaitInMillis, TimeUnit.MILLISECONDS)) { + mRunnable.run(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError("Unexpected InterruptedException", e); + } + } + } + + private static class PhoneCallEvent extends EventLogger.Event { + final boolean mIsInPhoneCall; + + PhoneCallEvent(boolean isInPhoneCall) { + mIsInPhoneCall = isInPhoneCall; + } + + @Override + public String eventToString() { + return "PhoneCallChange - inPhoneCall: " + mIsInPhoneCall; + } + } + + private static class SoundTriggerPowerEvent extends EventLogger.Event { + final int mSoundTriggerPowerState; + + SoundTriggerPowerEvent(int soundTriggerPowerState) { + mSoundTriggerPowerState = soundTriggerPowerState; + } + + @Override + public String eventToString() { + return "SoundTriggerPowerChange: " + stateToString(); + } + + private String stateToString() { + return switch (mSoundTriggerPowerState) { + case SOUND_TRIGGER_MODE_ALL_ENABLED -> "All enabled"; + case SOUND_TRIGGER_MODE_CRITICAL_ONLY -> "Critical only"; + case SOUND_TRIGGER_MODE_ALL_DISABLED -> "All disabled"; + default -> "Unknown power state: " + mSoundTriggerPowerState; + }; + } + } + + private static class DeviceStateEvent extends EventLogger.Event { + final SoundTriggerDeviceState mSoundTriggerDeviceState; + + DeviceStateEvent(SoundTriggerDeviceState soundTriggerDeviceState) { + mSoundTriggerDeviceState = soundTriggerDeviceState; + } + + @Override + public String eventToString() { + return "DeviceStateChange: " + mSoundTriggerDeviceState.name(); + } + } +} diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java new file mode 100644 index 000000000000..8773cabeeb92 --- /dev/null +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.soundtrigger; + +import android.telephony.Annotation; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Handles monitoring telephony call state across active subscriptions. + * + * @hide + */ +public class PhoneCallStateHandler { + + public interface Callback { + void onPhoneCallStateChanged(boolean isInPhoneCall); + } + + private final Object mLock = new Object(); + + // Actually never contended due to executor. + @GuardedBy("mLock") + private final List<MyCallStateListener> mListenerList = new ArrayList<>(); + + private final AtomicBoolean mIsPhoneCallOngoing = new AtomicBoolean(false); + + private final SubscriptionManager mSubscriptionManager; + private final TelephonyManager mTelephonyManager; + private final Callback mCallback; + + private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + + public PhoneCallStateHandler( + SubscriptionManager subscriptionManager, + TelephonyManager telephonyManager, + Callback callback) { + mSubscriptionManager = Objects.requireNonNull(subscriptionManager); + mTelephonyManager = Objects.requireNonNull(telephonyManager); + mCallback = Objects.requireNonNull(callback); + mSubscriptionManager.addOnSubscriptionsChangedListener( + mExecutor, + new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + updateTelephonyListeners(); + } + + @Override + public void onAddListenerFailed() { + Slog.wtf( + "SoundTriggerPhoneCallStateHandler", + "Failed to add a telephony listener"); + } + }); + } + + private final class MyCallStateListener extends TelephonyCallback + implements TelephonyCallback.CallStateListener { + + final TelephonyManager mTelephonyManagerForSubId; + + // Manager corresponding to the sub-id + MyCallStateListener(TelephonyManager telephonyManager) { + mTelephonyManagerForSubId = telephonyManager; + } + + void cleanup() { + mExecutor.execute(() -> mTelephonyManagerForSubId.unregisterTelephonyCallback(this)); + } + + @Override + public void onCallStateChanged(int unused) { + updateCallStatus(); + } + } + + /** Compute the current call status, and dispatch callback if it has changed. */ + private void updateCallStatus() { + boolean callStatus = checkCallStatus(); + if (mIsPhoneCallOngoing.compareAndSet(!callStatus, callStatus)) { + mCallback.onPhoneCallStateChanged(callStatus); + } + } + + /** + * Synchronously query the current telephony call state across all subscriptions + * + * @return - {@code true} if in call, {@code false} if not in call. + */ + private boolean checkCallStatus() { + List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList(); + if (infoList == null) return false; + return infoList.stream() + .filter(s -> (s.getSubscriptionId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) + .anyMatch(s -> isCallOngoingFromState( + mTelephonyManager + .createForSubscriptionId(s.getSubscriptionId()) + .getCallStateForSubscription())); + } + + private void updateTelephonyListeners() { + synchronized (mLock) { + for (var listener : mListenerList) { + listener.cleanup(); + } + mListenerList.clear(); + List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList(); + if (infoList == null) return; + infoList.stream() + .filter(s -> s.getSubscriptionId() + != SubscriptionManager.INVALID_SUBSCRIPTION_ID) + .map(s -> mTelephonyManager.createForSubscriptionId(s.getSubscriptionId())) + .forEach(manager -> { + synchronized (mLock) { + var listener = new MyCallStateListener(manager); + mListenerList.add(listener); + manager.registerTelephonyCallback(mExecutor, listener); + } + }); + } + } + + private static boolean isCallOngoingFromState(@Annotation.CallState int callState) { + return switch (callState) { + case TelephonyManager.CALL_STATE_IDLE, TelephonyManager.CALL_STATE_RINGING -> false; + case TelephonyManager.CALL_STATE_OFFHOOK -> true; + default -> throw new IllegalStateException( + "Received unexpected call state from Telephony Manager: " + callState); + }; + } +} diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 255db1e4db83..b4066ab1ff39 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -16,15 +16,12 @@ package com.android.server.soundtrigger; +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState; import static com.android.server.soundtrigger.SoundTriggerEvent.SessionEvent.Type; import static com.android.server.utils.EventLogger.Event.ALOGW; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.ModelParams; import android.hardware.soundtrigger.SoundTrigger; @@ -45,11 +42,7 @@ import android.os.DeadObjectException; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.SoundTriggerPowerSaveMode; import android.os.RemoteException; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -99,37 +92,20 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private SoundTriggerModule mModule; private final Object mLock = new Object(); private final Context mContext; - private final TelephonyManager mTelephonyManager; - private final PhoneStateListener mPhoneStateListener; - private final PowerManager mPowerManager; // The SoundTriggerManager layer handles multiple recognition models of type generic and // keyphrase. We store the ModelData here in a hashmap. - private final HashMap<UUID, ModelData> mModelDataMap; + private final HashMap<UUID, ModelData> mModelDataMap = new HashMap<>(); // An index of keyphrase sound models so that we can reach them easily. We support indexing // keyphrase sound models with a keyphrase ID. Sound model with the same keyphrase ID will // replace an existing model, thus there is a 1:1 mapping from keyphrase ID to a voice // sound model. - private HashMap<Integer, UUID> mKeyphraseUuidMap; - - private boolean mCallActive = false; - private @SoundTriggerPowerSaveMode int mSoundTriggerPowerSaveMode = - PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED; + private final HashMap<Integer, UUID> mKeyphraseUuidMap = new HashMap<>(); // Whether ANY recognition (keyphrase or generic) has been requested. private boolean mRecognitionRequested = false; - private PowerSaveModeListener mPowerSaveModeListener; - - - // Handler to process call state changes will delay to allow time for the audio - // and sound trigger HALs to process the end of call notifications - // before we re enable pending recognition requests. - private final Handler mHandler; - private static final int MSG_CALL_STATE_CHANGED = 0; - private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000; - // TODO(b/269366605) Temporary solution to query correct moduleProperties private final int mModuleId; private final Function<SoundTrigger.StatusListener, SoundTriggerModule> mModuleProvider; @@ -139,16 +115,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { @GuardedBy("mLock") private boolean mIsDetached = false; + @GuardedBy("mLock") + private SoundTriggerDeviceState mDeviceState = SoundTriggerDeviceState.DISABLE; + SoundTriggerHelper(Context context, EventLogger eventLogger, @NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider, int moduleId, @NonNull Supplier<List<ModuleProperties>> modulePropertiesProvider) { mModuleId = moduleId; mContext = context; - mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mModelDataMap = new HashMap<UUID, ModelData>(); - mKeyphraseUuidMap = new HashMap<Integer, UUID>(); mModuleProvider = moduleProvider; mEventLogger = eventLogger; mModulePropertiesProvider = modulePropertiesProvider; @@ -157,31 +132,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } else { mModule = mModuleProvider.apply(this); } - Looper looper = Looper.myLooper(); - if (looper == null) { - looper = Looper.getMainLooper(); - } - mPhoneStateListener = new MyCallStateListener(looper); - if (looper != null) { - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_CALL_STATE_CHANGED: - synchronized (mLock) { - onCallStateChangedLocked( - TelephonyManager.CALL_STATE_OFFHOOK == msg.arg1); - } - break; - default: - Slog.e(TAG, "unknown message in handler:" + msg.what); - break; - } - } - }; - } else { - mHandler = null; - } } /** @@ -373,7 +323,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.setSoundModel(soundModel); if (!isRecognitionAllowedByDeviceState(modelData)) { - initializeDeviceStateListeners(); return STATUS_OK; } @@ -497,11 +446,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.setLoaded(); modelData.clearCallback(); modelData.setRecognitionConfig(null); - - if (!computeRecognitionRequestedLocked()) { - internalClearGlobalStateLocked(); - } - return status; } } @@ -638,6 +582,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } + public void onDeviceStateChanged(SoundTriggerDeviceState state) { + synchronized (mLock) { + if (mIsDetached || mDeviceState == state) { + // Nothing to update + return; + } + mDeviceState = state; + updateAllRecognitionsLocked(); + } + } + public int getGenericModelState(UUID modelId) { synchronized (mLock) { MetricsLogger.count(mContext, "sth_get_generic_model_state", 1); @@ -880,25 +835,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - private void onCallStateChangedLocked(boolean callActive) { - if (mCallActive == callActive) { - // We consider multiple call states as being active - // so we check if something really changed or not here. - return; - } - mCallActive = callActive; - updateAllRecognitionsLocked(); - } - - private void onPowerSaveModeChangedLocked( - @SoundTriggerPowerSaveMode int soundTriggerPowerSaveMode) { - if (mSoundTriggerPowerSaveMode == soundTriggerPowerSaveMode) { - return; - } - mSoundTriggerPowerSaveMode = soundTriggerPowerSaveMode; - updateAllRecognitionsLocked(); - } - private void onModelUnloadedLocked(int modelHandle) { ModelData modelData = getModelDataForLocked(modelHandle); if (modelData != null) { @@ -1011,10 +947,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return status; } status = startRecognitionLocked(model, notifyClientOnError); - // Initialize power save, call active state monitoring logic. - if (status == STATUS_OK) { - initializeDeviceStateListeners(); - } return status; } else { return stopRecognitionLocked(model, notifyClientOnError); @@ -1040,7 +972,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } finally { internalClearModelStateLocked(); - internalClearGlobalStateLocked(); if (mModule != null) { mModule.detach(); try { @@ -1054,24 +985,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - // internalClearGlobalStateLocked() cleans up the telephony and power save listeners. - private void internalClearGlobalStateLocked() { - // Unregister from call state changes. - final long token = Binder.clearCallingIdentity(); - try { - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); - } finally { - Binder.restoreCallingIdentity(token); - } - - // Unregister from power save mode changes. - if (mPowerSaveModeListener != null) { - mContext.unregisterReceiver(mPowerSaveModeListener); - mPowerSaveModeListener = null; - } - mRecognitionRequested = false; - } - // Clears state for all models (generic and keyphrase). private void internalClearModelStateLocked() { for (ModelData modelData : mModelDataMap.values()) { @@ -1079,67 +992,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - class MyCallStateListener extends PhoneStateListener { - MyCallStateListener(@NonNull Looper looper) { - super(Objects.requireNonNull(looper)); - } - - @Override - public void onCallStateChanged(int state, String arg1) { - - if (mHandler != null) { - synchronized (mLock) { - mHandler.removeMessages(MSG_CALL_STATE_CHANGED); - Message msg = mHandler.obtainMessage(MSG_CALL_STATE_CHANGED, state, 0); - mHandler.sendMessageDelayed( - msg, (TelephonyManager.CALL_STATE_OFFHOOK == state) ? 0 - : CALL_INACTIVE_MSG_DELAY_MS); - } - } - } - } - - class PowerSaveModeListener extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (!PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { - return; - } - @SoundTriggerPowerSaveMode int soundTriggerPowerSaveMode = - mPowerManager.getSoundTriggerPowerSaveMode(); - synchronized (mLock) { - onPowerSaveModeChangedLocked(soundTriggerPowerSaveMode); - } - } - } - - private void initializeDeviceStateListeners() { - if (mRecognitionRequested) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - // Get the current call state synchronously for the first recognition. - mCallActive = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK; - - // Register for call state changes when the first call to start recognition occurs. - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); - - // Register for power saver mode changes when the first call to start recognition - // occurs. - if (mPowerSaveModeListener == null) { - mPowerSaveModeListener = new PowerSaveModeListener(); - mContext.registerReceiver(mPowerSaveModeListener, - new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); - } - mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode(); - - mRecognitionRequested = true; - } finally { - Binder.restoreCallingIdentity(token); - } - } - /** * Stops and unloads all models. This is intended as a clean-up call with the expectation that * this instance is not used after. @@ -1153,7 +1005,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { forceStopAndUnloadModelLocked(model, null); } mModelDataMap.clear(); - internalClearGlobalStateLocked(); if (mModule != null) { mModule.detach(); mModule = null; @@ -1305,28 +1156,14 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { * @param modelData Model data to be used for recognition * @return True if recognition is allowed to run at this time. False if not. */ + @GuardedBy("mLock") private boolean isRecognitionAllowedByDeviceState(ModelData modelData) { - // if mRecognitionRequested is false, call and power state listeners are not registered so - // we read current state directly from services - if (!mRecognitionRequested) { - mCallActive = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK; - mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode(); - } - - return !mCallActive && isRecognitionAllowedByPowerState( - modelData); - } - - /** - * Helper function to validate if a recognition should run based on the current power state - * - * @param modelData Model data to be used for recognition - * @return True if device state allows recognition to run, false if not. - */ - private boolean isRecognitionAllowedByPowerState(ModelData modelData) { - return mSoundTriggerPowerSaveMode == PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED - || (mSoundTriggerPowerSaveMode == PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY - && modelData.shouldRunInBatterySaverMode()); + return switch (mDeviceState) { + case DISABLE -> false; + case CRITICAL -> modelData.shouldRunInBatterySaverMode(); + case ENABLE -> true; + default -> throw new AssertionError("Enum changed between compile and runtime"); + }; } // A single routine that implements the start recognition logic for both generic and keyphrase diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 913535e06a21..3151781ff7ba 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -35,14 +35,18 @@ import static com.android.server.soundtrigger.SoundTriggerEvent.SessionEvent.Typ import static com.android.server.utils.EventLogger.Event.ALOGW; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import static com.android.server.soundtrigger.DeviceStateHandler.DeviceStateListener; +import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.ServiceConnection; import android.content.pm.PackageManager; @@ -85,6 +89,8 @@ import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -112,6 +118,8 @@ import java.util.Objects; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -131,8 +139,8 @@ public class SoundTriggerService extends SystemService { private static final boolean DEBUG = true; private static final int SESSION_MAX_EVENT_SIZE = 128; - final Context mContext; - private Object mLock; + private final Context mContext; + private final Object mLock = new Object(); private final SoundTriggerServiceStub mServiceStub; private final LocalSoundTriggerService mLocalSoundTriggerService; @@ -140,6 +148,7 @@ public class SoundTriggerService extends SystemService { private SoundTriggerDbHelper mDbHelper; private final EventLogger mServiceEventLogger = new EventLogger(256, "Service"); + private final EventLogger mDeviceEventLogger = new EventLogger(256, "Device Event"); private final Set<EventLogger> mSessionEventLoggers = ConcurrentHashMap.newKeySet(4); private final Deque<EventLogger> mDetachedSessionEventLoggers = new LinkedBlockingDeque<>(4); @@ -223,13 +232,18 @@ public class SoundTriggerService extends SystemService { @GuardedBy("mLock") private final ArrayMap<String, NumOps> mNumOpsPerPackage = new ArrayMap<>(); + private final DeviceStateHandler mDeviceStateHandler; + private final Executor mDeviceStateHandlerExecutor = Executors.newSingleThreadExecutor(); + private PhoneCallStateHandler mPhoneCallStateHandler; + public SoundTriggerService(Context context) { super(context); mContext = context; mServiceStub = new SoundTriggerServiceStub(); mLocalSoundTriggerService = new LocalSoundTriggerService(context); - mLock = new Object(); mSoundModelStatTracker = new SoundModelStatTracker(); + mDeviceStateHandler = new DeviceStateHandler(mDeviceStateHandlerExecutor, + mDeviceEventLogger); } @Override @@ -243,6 +257,29 @@ public class SoundTriggerService extends SystemService { Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode()); if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) { mDbHelper = new SoundTriggerDbHelper(mContext); + final PowerManager powerManager = mContext.getSystemService(PowerManager.class); + // Hook up power state listener + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!PowerManager.ACTION_POWER_SAVE_MODE_CHANGED + .equals(intent.getAction())) { + return; + } + mDeviceStateHandler.onPowerModeChanged( + powerManager.getSoundTriggerPowerSaveMode()); + } + }, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); + // Initialize the initial power state + // Do so after registering the listener so we ensure that we don't drop any events + mDeviceStateHandler.onPowerModeChanged(powerManager.getSoundTriggerPowerSaveMode()); + + // PhoneCallStateHandler initializes the original call state + mPhoneCallStateHandler = new PhoneCallStateHandler( + mContext.getSystemService(SubscriptionManager.class), + mContext.getSystemService(TelephonyManager.class), + mDeviceStateHandler); } mMiddlewareService = ISoundTriggerMiddlewareService.Stub.asInterface( ServiceManager.waitForService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE)); @@ -380,6 +417,9 @@ public class SoundTriggerService extends SystemService { // Event loggers pw.println("##Service-Wide logs:"); mServiceEventLogger.dump(pw, /* indent = */ " "); + pw.println("\n##Device state logs:"); + mDeviceStateHandler.dump(pw); + mDeviceEventLogger.dump(pw, /* indent = */ " "); pw.println("\n##Active Session dumps:\n"); for (var sessionLogger : mSessionEventLoggers) { @@ -403,6 +443,7 @@ public class SoundTriggerService extends SystemService { class SoundTriggerSessionStub extends ISoundTriggerSession.Stub { private final SoundTriggerHelper mSoundTriggerHelper; + private final DeviceStateListener mListener; // Used to detect client death. private final IBinder mClient; private final Identity mOriginatorIdentity; @@ -424,6 +465,9 @@ public class SoundTriggerService extends SystemService { } catch (RemoteException e) { clientDied(); } + mListener = (SoundTriggerDeviceState state) + -> mSoundTriggerHelper.onDeviceStateChanged(state); + mDeviceStateHandler.registerListener(mListener); } @Override @@ -874,6 +918,7 @@ public class SoundTriggerService extends SystemService { } private void detach() { + mDeviceStateHandler.unregisterListener(mListener); mSoundTriggerHelper.detach(); detachSessionLogger(mEventLogger); } @@ -890,7 +935,8 @@ public class SoundTriggerService extends SystemService { private void enforceDetectionPermissions(ComponentName detectionService) { PackageManager packageManager = mContext.getPackageManager(); String packageName = detectionService.getPackageName(); - if (packageManager.checkPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName) + if (packageManager.checkPermission( + Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(detectionService.getPackageName() + " does not have" + " permission " + Manifest.permission.CAPTURE_AUDIO_HOTWORD); @@ -1576,6 +1622,7 @@ public class SoundTriggerService extends SystemService { private final @NonNull IBinder mClient; private final EventLogger mEventLogger; private final Identity mOriginatorIdentity; + private final @NonNull DeviceStateListener mListener; private final SparseArray<UUID> mModelUuid = new SparseArray<>(1); @@ -1594,6 +1641,9 @@ public class SoundTriggerService extends SystemService { } catch (RemoteException e) { clientDied(); } + mListener = (SoundTriggerDeviceState state) + -> mSoundTriggerHelper.onDeviceStateChanged(state); + mDeviceStateHandler.registerListener(mListener); } @Override @@ -1662,6 +1712,7 @@ public class SoundTriggerService extends SystemService { private void detachInternal() { mEventLogger.enqueue(new SessionEvent(Type.DETACH, null)); detachSessionLogger(mEventLogger); + mDeviceStateHandler.unregisterListener(mListener); mSoundTriggerHelper.detach(); } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AidlUtil.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AidlUtil.java index f3457f5a221b..56a159eb465a 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AidlUtil.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/AidlUtil.java @@ -21,6 +21,8 @@ import android.media.soundtrigger.PhraseRecognitionExtra; import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModelType; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; /** * Utilities for working with sound trigger related AIDL generated types. @@ -49,23 +51,29 @@ public class AidlUtil { /** * Creates a new generic abort event. + * * @return The new event. */ - static RecognitionEvent newAbortEvent() { - RecognitionEvent event = newEmptyRecognitionEvent(); - event.type = SoundModelType.GENERIC; - event.status = RecognitionStatus.ABORTED; - return event; + static RecognitionEventSys newAbortEvent() { + RecognitionEvent recognitionEvent = newEmptyRecognitionEvent(); + recognitionEvent.type = SoundModelType.GENERIC; + recognitionEvent.status = RecognitionStatus.ABORTED; + RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); + recognitionEventSys.recognitionEvent = recognitionEvent; + return recognitionEventSys; } /** * Creates a new generic phrase event. + * * @return The new event. */ - static PhraseRecognitionEvent newAbortPhraseEvent() { - PhraseRecognitionEvent event = newEmptyPhraseRecognitionEvent(); - event.common.type = SoundModelType.KEYPHRASE; - event.common.status = RecognitionStatus.ABORTED; - return event; + static PhraseRecognitionEventSys newAbortPhraseEvent() { + PhraseRecognitionEvent recognitionEvent = newEmptyPhraseRecognitionEvent(); + recognitionEvent.common.type = SoundModelType.KEYPHRASE; + recognitionEvent.common.status = RecognitionStatus.ABORTED; + PhraseRecognitionEventSys phraseRecognitionEventSys = new PhraseRecognitionEventSys(); + phraseRecognitionEventSys.phraseRecognitionEvent = recognitionEvent; + return phraseRecognitionEventSys; } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java index 75206e69bc6a..6f4a94692c4b 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java @@ -20,12 +20,12 @@ import android.hardware.soundtrigger3.ISoundTriggerHw; import android.hardware.soundtrigger3.ISoundTriggerHwCallback; import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.SoundModel; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.IBinder; /** @@ -173,14 +173,19 @@ interface ISoundTriggerHal { */ interface ModelCallback { /** - * @see ISoundTriggerHwCallback#recognitionCallback(int, RecognitionEvent) + * Decorated callback of + * {@link ISoundTriggerHwCallback#recognitionCallback(int, RecognitionEvent)} where + * {@link RecognitionEventSys} is decorating the returned {@link RecognitionEvent} */ - void recognitionCallback(int modelHandle, RecognitionEvent event); + void recognitionCallback(int modelHandle, RecognitionEventSys event); /** - * @see ISoundTriggerHwCallback#phraseRecognitionCallback(int, PhraseRecognitionEvent) + * Decorated callback of + * {@link ISoundTriggerHwCallback#phraseRecognitionCallback(int, PhraseRecognitionEvent)} + * where {@link PhraseRecognitionEventSys} is decorating the returned + * {@link PhraseRecognitionEvent} */ - void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event); + void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEventSys event); /** * @see ISoundTriggerHwCallback#modelUnloaded(int) diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java index 8c7cabeee320..d8ef2b6640fd 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java @@ -19,14 +19,14 @@ package com.android.server.soundtrigger_middleware; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.SoundModelType; import android.media.soundtrigger.Status; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.IBinder; import java.util.HashSet; @@ -238,13 +238,13 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal } @Override - public void recognitionCallback(int modelHandle, RecognitionEvent event) { + public void recognitionCallback(int modelHandle, RecognitionEventSys event) { synchronized (mActiveModels) { if (!mActiveModels.contains(modelHandle)) { // Discard the event. return; } - if (!event.recognitionStillActive) { + if (!event.recognitionEvent.recognitionStillActive) { mActiveModels.remove(modelHandle); } // A recognition event must be the last one for its model, unless it indicates that @@ -255,13 +255,13 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal } @Override - public void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event) { + public void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEventSys event) { synchronized (mActiveModels) { if (!mActiveModels.contains(modelHandle)) { // Discard the event. return; } - if (!event.common.recognitionStillActive) { + if (!event.phraseRecognitionEvent.common.recognitionStillActive) { mActiveModels.remove(modelHandle); } // A recognition event must be the last one for its model, unless it indicates that diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java index 24741e1caea9..bac24669696c 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java @@ -17,14 +17,14 @@ package com.android.server.soundtrigger_middleware; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.Status; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.DeadObjectException; import android.os.IBinder; import android.util.Log; @@ -253,7 +253,7 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal { } @Override - public void recognitionCallback(int model, RecognitionEvent event) { + public void recognitionCallback(int model, RecognitionEventSys event) { synchronized (mModelStates) { ModelState state = mModelStates.get(model); if (state == null || state == ModelState.INACTIVE) { @@ -261,15 +261,16 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal { reboot(); return; } - if (event.recognitionStillActive && event.status != RecognitionStatus.SUCCESS - && event.status != RecognitionStatus.FORCED) { + if (event.recognitionEvent.recognitionStillActive + && event.recognitionEvent.status != RecognitionStatus.SUCCESS + && event.recognitionEvent.status != RecognitionStatus.FORCED) { Log.wtfStack(TAG, "recognitionStillActive is only allowed when the recognition status " + "is SUCCESS"); reboot(); return; } - if (!event.recognitionStillActive) { + if (!event.recognitionEvent.recognitionStillActive) { mModelStates.replace(model, ModelState.INACTIVE); } } @@ -278,7 +279,7 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal { } @Override - public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) { + public void phraseRecognitionCallback(int model, PhraseRecognitionEventSys event) { synchronized (mModelStates) { ModelState state = mModelStates.get(model); if (state == null || state == ModelState.INACTIVE) { @@ -286,16 +287,16 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal { reboot(); return; } - if (event.common.recognitionStillActive - && event.common.status != RecognitionStatus.SUCCESS - && event.common.status != RecognitionStatus.FORCED) { + if (event.phraseRecognitionEvent.common.recognitionStillActive + && event.phraseRecognitionEvent.common.status != RecognitionStatus.SUCCESS + && event.phraseRecognitionEvent.common.status != RecognitionStatus.FORCED) { Log.wtfStack(TAG, "recognitionStillActive is only allowed when the recognition status " + "is SUCCESS"); reboot(); return; } - if (!event.common.recognitionStillActive) { + if (!event.phraseRecognitionEvent.common.recognitionStillActive) { mModelStates.replace(model, ModelState.INACTIVE); } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java index c67bdd76eee8..df2e9b41662b 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java @@ -25,9 +25,12 @@ import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.Status; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.IBinder; import android.os.IHwBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.system.OsConstants; import android.util.Log; @@ -570,16 +573,20 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal { public void recognitionCallback_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event, int cookie) { - mDelegate.recognitionCallback(event.header.model, - ConversionUtil.hidl2aidlRecognitionEvent(event)); + RecognitionEventSys eventSys = new RecognitionEventSys(); + eventSys.recognitionEvent = ConversionUtil.hidl2aidlRecognitionEvent(event); + eventSys.halEventReceivedMillis = SystemClock.elapsedRealtime(); + mDelegate.recognitionCallback(event.header.model, eventSys); } @Override public void phraseRecognitionCallback_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event, int cookie) { - mDelegate.phraseRecognitionCallback(event.common.header.model, - ConversionUtil.hidl2aidlPhraseRecognitionEvent(event)); + PhraseRecognitionEventSys eventSys = new PhraseRecognitionEventSys(); + eventSys.phraseRecognitionEvent = ConversionUtil.hidl2aidlPhraseRecognitionEvent(event); + eventSys.halEventReceivedMillis = SystemClock.elapsedRealtime(); + mDelegate.phraseRecognitionCallback(event.common.header.model, eventSys); } @Override diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java index 8bb5eb191858..b1165bb57628 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java @@ -29,9 +29,12 @@ import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.Status; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.os.SystemClock; public class SoundTriggerHw3Compat implements ISoundTriggerHal { private final @NonNull ISoundTriggerHw mDriver; @@ -244,14 +247,20 @@ public class SoundTriggerHw3Compat implements ISoundTriggerHal { public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) { // A FORCED status implies that recognition is still active after the event. event.common.recognitionStillActive |= event.common.status == RecognitionStatus.FORCED; - mDelegate.phraseRecognitionCallback(model, event); + PhraseRecognitionEventSys phraseRecognitionEventSys = new PhraseRecognitionEventSys(); + phraseRecognitionEventSys.phraseRecognitionEvent = event; + phraseRecognitionEventSys.halEventReceivedMillis = SystemClock.elapsedRealtimeNanos(); + mDelegate.phraseRecognitionCallback(model, phraseRecognitionEventSys); } @Override public void recognitionCallback(int model, RecognitionEvent event) { // A FORCED status implies that recognition is still active after the event. event.recognitionStillActive |= event.status == RecognitionStatus.FORCED; - mDelegate.recognitionCallback(model, event); + RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); + recognitionEventSys.recognitionEvent = event; + recognitionEventSys.halEventReceivedMillis = SystemClock.elapsedRealtimeNanos(); + mDelegate.recognitionCallback(model, recognitionEventSys); } @Override diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java index 0e796d1afbd6..2ee4e3cff02c 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -16,12 +16,24 @@ package com.android.server.soundtrigger_middleware; -import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.*; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.DETACH; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.FORCE_RECOGNITION; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.GET_MODEL_PARAMETER; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.LOAD_MODEL; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.LOAD_PHRASE_MODEL; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.MODEL_UNLOADED; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.MODULE_DIED; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.QUERY_MODEL_PARAMETER; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.RECOGNITION; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.RESOURCES_AVAILABLE; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.SET_MODEL_PARAMETER; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.START_RECOGNITION; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.STOP_RECOGNITION; +import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.UNLOAD_MODEL; import static com.android.server.utils.EventLogger.Event.ALOGI; import static com.android.server.utils.EventLogger.Event.ALOGW; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.Context; import android.media.permission.Identity; import android.media.permission.IdentityContext; @@ -29,11 +41,12 @@ import android.media.soundtrigger.ModelParameterRange; import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.os.BatteryStatsInternal; import android.os.IBinder; @@ -45,19 +58,18 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.LatencyTracker; import com.android.server.LocalServices; -import com.android.server.utils.EventLogger.Event; import com.android.server.utils.EventLogger; - +import com.android.server.utils.EventLogger.Event; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Deque; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; -import java.util.Deque; /** @@ -370,7 +382,7 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt } @Override - public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession) + public void onRecognition(int modelHandle, RecognitionEventSys event, int captureSession) throws RemoteException { try { mBatteryStatsInternalSupplier.get().noteWakingSoundTrigger( @@ -388,13 +400,13 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt } @Override - public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event, + public void onPhraseRecognition(int modelHandle, PhraseRecognitionEventSys event, int captureSession) throws RemoteException { try { mBatteryStatsInternalSupplier.get().noteWakingSoundTrigger( SystemClock.elapsedRealtime(), mOriginatorIdentity.uid); - startKeyphraseEventLatencyTracking(event); + startKeyphraseEventLatencyTracking(event.phraseRecognitionEvent); mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession); mEventLogger.enqueue(SessionEvent.createForVoid( RECOGNITION, modelHandle, event, captureSession) diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java index 00cedd77414e..00b894e1c6b5 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java @@ -27,15 +27,15 @@ import android.media.permission.Identity; import android.media.permission.IdentityContext; import android.media.permission.PermissionUtil; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.Status; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.os.IBinder; import android.os.RemoteException; @@ -307,16 +307,15 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware } @Override - public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession) - throws RemoteException { + public void onRecognition(int modelHandle, RecognitionEventSys event, + int captureSession) throws RemoteException { enforcePermissions("Sound trigger recognition."); mDelegate.onRecognition(modelHandle, event, captureSession); } @Override - public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event, - int captureSession) - throws RemoteException { + public void onPhraseRecognition(int modelHandle, PhraseRecognitionEventSys event, + int captureSession) throws RemoteException { enforcePermissions("Sound trigger phrase recognition."); mDelegate.onPhraseRecognition(modelHandle, event, captureSession); } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java index 15c9ba923d3a..f208c03024b2 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java @@ -21,17 +21,17 @@ import android.annotation.Nullable; import android.media.permission.Identity; import android.media.permission.IdentityContext; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.Status; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.os.IBinder; import android.os.RemoteException; @@ -710,8 +710,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware } } - class CallbackWrapper implements ISoundTriggerCallback, - IBinder.DeathRecipient { + class CallbackWrapper implements ISoundTriggerCallback, IBinder.DeathRecipient { private final ISoundTriggerCallback mCallback; CallbackWrapper(ISoundTriggerCallback callback) { @@ -728,11 +727,11 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware } @Override - public void onRecognition(int modelHandle, @NonNull RecognitionEvent event, + public void onRecognition(int modelHandle, @NonNull RecognitionEventSys event, int captureSession) { synchronized (SoundTriggerMiddlewareValidation.this) { ModelState modelState = mLoadedModels.get(modelHandle); - if (!event.recognitionStillActive) { + if (!event.recognitionEvent.recognitionStillActive) { modelState.activityState = ModelState.Activity.LOADED; } } @@ -744,7 +743,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware Log.w(TAG, "Client callback exception.", e); synchronized (SoundTriggerMiddlewareValidation.this) { ModelState modelState = mLoadedModels.get(modelHandle); - if (event.status != RecognitionStatus.FORCED) { + if (event.recognitionEvent.status != RecognitionStatus.FORCED) { modelState.activityState = ModelState.Activity.INTERCEPTED; // If we failed to deliver an actual event to the client, they would // never know to restart it whenever circumstances change. Thus, we @@ -758,10 +757,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware @Override public void onPhraseRecognition(int modelHandle, - @NonNull PhraseRecognitionEvent event, int captureSession) { + @NonNull PhraseRecognitionEventSys event, int captureSession) { synchronized (SoundTriggerMiddlewareValidation.this) { ModelState modelState = mLoadedModels.get(modelHandle); - if (!event.common.recognitionStillActive) { + if (!event.phraseRecognitionEvent.common.recognitionStillActive) { modelState.activityState = ModelState.Activity.LOADED; } } @@ -773,7 +772,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware Log.w(TAG, "Client callback exception.", e); synchronized (SoundTriggerMiddlewareValidation.this) { ModelState modelState = mLoadedModels.get(modelHandle); - if (!event.common.recognitionStillActive) { + if (!event.phraseRecognitionEvent.common.recognitionStillActive) { modelState.activityState = ModelState.Activity.INTERCEPTED; // If we failed to deliver an actual event to the client, they would // never know to restart it whenever circumstances change. Thus, we diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index 6223b2e8ace4..84cec5592831 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -19,16 +19,16 @@ package com.android.server.soundtrigger_middleware; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.SoundModelType; import android.media.soundtrigger.Status; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; +import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -499,10 +499,10 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo @Override public void recognitionCallback(int modelHandle, - @NonNull RecognitionEvent recognitionEvent) { + @NonNull RecognitionEventSys event) { ISoundTriggerCallback callback; synchronized (SoundTriggerModule.this) { - if (!recognitionEvent.recognitionStillActive) { + if (!event.recognitionEvent.recognitionStillActive) { setState(ModelState.LOADED); } callback = mCallback; @@ -510,7 +510,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo // The callback must be invoked outside of the lock. try { if (callback != null) { - callback.onRecognition(mHandle, recognitionEvent, mSession.mSessionHandle); + callback.onRecognition(mHandle, event, mSession.mSessionHandle); } } catch (RemoteException e) { // We're not expecting any exceptions here. @@ -520,10 +520,10 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo @Override public void phraseRecognitionCallback(int modelHandle, - @NonNull PhraseRecognitionEvent phraseRecognitionEvent) { + @NonNull PhraseRecognitionEventSys event) { ISoundTriggerCallback callback; synchronized (SoundTriggerModule.this) { - if (!phraseRecognitionEvent.common.recognitionStillActive) { + if (!event.phraseRecognitionEvent.common.recognitionStillActive) { setState(ModelState.LOADED); } callback = mCallback; @@ -532,8 +532,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo // The callback must be invoked outside of the lock. try { if (callback != null) { - mCallback.onPhraseRecognition(mHandle, phraseRecognitionEvent, - mSession.mSessionHandle); + mCallback.onPhraseRecognition(mHandle, event, mSession.mSessionHandle); } } catch (RemoteException e) { // We're not expecting any exceptions here. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt index 6053f1dc9319..daecfe7e4c4d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.PlatformConsts import android.tools.common.traces.component.ComponentNameMatcher -import android.tools.common.traces.wm.WindowManagerState.Companion.STATE_RESUMED import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.helpers.FIND_TIMEOUT import android.tools.device.traces.parsers.WindowManagerStateHelper @@ -54,8 +54,8 @@ constructor( launchButton.click() wmHelper .StateSyncBuilder() - .withActivityState(SECONDARY_ACTIVITY_COMPONENT, STATE_RESUMED) - .withActivityState(MAIN_ACTIVITY_COMPONENT, STATE_RESUMED) + .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) .waitForAndVerify() } @@ -73,8 +73,8 @@ constructor( launchButton.click() wmHelper .StateSyncBuilder() - .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, STATE_RESUMED) - .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, STATE_RESUMED) + .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED) .waitForAndVerify() } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java index 017de605fe39..e2d17cdbe9e6 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java @@ -26,39 +26,57 @@ import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.RenderNode; import android.graphics.Shader; +import android.graphics.SurfaceTexture; import android.hardware.HardwareBuffer; import android.media.Image; import android.media.ImageWriter; import android.os.Bundle; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.TextureView; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; @SuppressWarnings({"UnusedDeclaration"}) public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callback, - AdapterView.OnItemSelectedListener { + TextureView.SurfaceTextureListener { private static final int WIDTH = 512; private static final int HEIGHT = 512; private ImageView mImageView; private SurfaceView mSurfaceView; + private TextureView mTextureView; private HardwareBuffer mGradientBuffer; - private ImageWriter mImageWriter; + private Map<View, ImageWriter> mImageWriters = new HashMap<>(); private ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); private String[] mColorNames = {"sRGB", "BT2020_HLG", "BT2020_PQ"}; - private String mCurrentColorName = "sRGB"; - private FutureTask<HardwareBuffer> authorGradientBuffer(HardwareBuffer buffer) { + private int mGradientEndColor = 0xFFFFFFFF; + + private int[] mGradientEndColors = {0xFFFFFFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF}; + private String[] mGradientColorNames = {"Grayscale", "Red", "Green", "Blue"}; + + private final ExecutorService mBufferFenceExecutor = Executors.newFixedThreadPool(1); + private final ExecutorService mBufferExecutor = Executors.newFixedThreadPool(1); + + private FutureTask<HardwareBuffer> authorGradientBuffer( + HardwareBuffer buffer, int gradentEndColor) { HardwareBufferRenderer renderer = new HardwareBufferRenderer(buffer); RenderNode node = new RenderNode("content"); node.setPosition(0, 0, buffer.getWidth(), buffer.getHeight()); @@ -66,9 +84,10 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb Canvas canvas = node.beginRecording(); LinearGradient gradient = new LinearGradient( 0, 0, buffer.getWidth(), buffer.getHeight(), 0xFF000000, - 0xFFFFFFFF, Shader.TileMode.CLAMP); + gradentEndColor, Shader.TileMode.CLAMP); Paint paint = new Paint(); paint.setShader(gradient); + paint.setDither(true); canvas.drawRect(0f, 0f, buffer.getWidth(), buffer.getHeight(), paint); node.endRecording(); @@ -78,7 +97,7 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb FutureTask<HardwareBuffer> resolvedBuffer = new FutureTask<>(() -> buffer); renderer.obtainRenderRequest() .setColorSpace(colorSpace) - .draw(Executors.newSingleThreadExecutor(), result -> { + .draw(mBufferFenceExecutor, result -> { result.getFence().await(Duration.ofSeconds(3)); resolvedBuffer.run(); }); @@ -90,7 +109,7 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb WIDTH, HEIGHT, PixelFormat.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT); - return authorGradientBuffer(buffer); + return authorGradientBuffer(buffer, mGradientEndColor); } @Override @@ -101,29 +120,70 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); - ArrayAdapter<String> adapter = new ArrayAdapter<>( + ArrayAdapter<String> colorSpaceAdapter = new ArrayAdapter<>( this, android.R.layout.simple_spinner_item, mColorNames); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - Spinner spinner = new Spinner(this); - spinner.setAdapter(adapter); - spinner.setOnItemSelectedListener(this); + colorSpaceAdapter + .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + Spinner colorSpaceSpinner = new Spinner(this); + colorSpaceSpinner.setAdapter(colorSpaceAdapter); + colorSpaceSpinner.setOnItemSelectedListener(new ColorSpaceOnItemSelectedListener()); + + ArrayAdapter<String> gradientColorAdapter = new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, mGradientColorNames); + + gradientColorAdapter + .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + Spinner gradientColorSpinner = new Spinner(this); + gradientColorSpinner.setAdapter(gradientColorAdapter); + gradientColorSpinner + .setOnItemSelectedListener(new GradientColorOnItemSelectedListener()); mGradientBuffer = getGradientBuffer().get(); LinearLayout linearLayout = new LinearLayout(this); linearLayout.setOrientation(LinearLayout.VERTICAL); + TextView imageViewText = new TextView(this); + imageViewText.setText("ImageView"); mImageView = new ImageView(this); + TextView textureViewText = new TextView(this); + textureViewText.setText("TextureView"); + mTextureView = new TextureView(this); + mTextureView.setSurfaceTextureListener(this); + + TextView surfaceViewText = new TextView(this); + surfaceViewText.setText("SurfaceView"); mSurfaceView = new SurfaceView(this); mSurfaceView.getHolder().addCallback(this); - linearLayout.addView(spinner, new LinearLayout.LayoutParams( + LinearLayout spinnerLayout = new LinearLayout(this); + spinnerLayout.setOrientation(LinearLayout.HORIZONTAL); + + spinnerLayout.addView(colorSpaceSpinner, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + spinnerLayout.addView(gradientColorSpinner, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + linearLayout.addView(spinnerLayout, new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + linearLayout.addView(imageViewText, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); linearLayout.addView(mImageView, new LinearLayout.LayoutParams(WIDTH, HEIGHT)); + linearLayout.addView(textureViewText, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + linearLayout.addView(mTextureView, new LinearLayout.LayoutParams(WIDTH, HEIGHT)); + linearLayout.addView(surfaceViewText, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); linearLayout.addView(mSurfaceView, new LinearLayout.LayoutParams(WIDTH, HEIGHT)); setContentView(linearLayout); @@ -145,16 +205,24 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb } private void populateBuffers() { - Bitmap bitmap = Bitmap.wrapHardwareBuffer( - mGradientBuffer, ColorSpace.get(ColorSpace.Named.SRGB)); - Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, false); - copy.setColorSpace(mColorSpace); - mImageView.setImageBitmap(copy); - - try (Image image = mImageWriter.dequeueInputImage()) { - authorGradientBuffer(image.getHardwareBuffer()).get(); - image.setDataSpace(mColorSpace.getDataSpace()); - mImageWriter.queueInputImage(image); + try { + Bitmap bitmap = Bitmap.wrapHardwareBuffer( + getGradientBuffer().get(), ColorSpace.get(ColorSpace.Named.SRGB)); + Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, false); + copy.setColorSpace(mColorSpace); + mImageView.setImageBitmap(copy); + + for (ImageWriter writer : mImageWriters.values()) { + mBufferExecutor.execute(() -> { + try (Image image = writer.dequeueInputImage()) { + authorGradientBuffer(image.getHardwareBuffer(), mGradientEndColor).get(); + image.setDataSpace(mColorSpace.getDataSpace()); + writer.queueInputImage(image); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } } catch (Exception e) { throw new RuntimeException(e); } @@ -167,30 +235,81 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - mImageWriter = new ImageWriter.Builder(holder.getSurface()) + mImageWriters.put(mSurfaceView, new ImageWriter.Builder(holder.getSurface()) .setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT | HardwareBuffer.USAGE_COMPOSER_OVERLAY) - .build(); + .build()); populateBuffers(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { - mImageWriter.close(); - mImageWriter = null; + if (mImageWriters.containsKey(mSurfaceView)) { + mImageWriters.remove(mSurfaceView); + } } - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - mCurrentColorName = mColorNames[position]; - mColorSpace = getFromName(mCurrentColorName); + public void onSurfaceTextureAvailable( + @NonNull SurfaceTexture surface, int width, int height) { + mImageWriters.put(mTextureView, new ImageWriter.Builder(new Surface(surface)) + .setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE + | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT) + .build()); populateBuffers(); } @Override - public void onNothingSelected(AdapterView<?> parent) { + public void onSurfaceTextureSizeChanged( + @NonNull SurfaceTexture surface, int width, int height) { + + } + + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + if (mImageWriters.containsKey(mTextureView)) { + mImageWriters.remove(mTextureView); + } + return false; + } + + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { + + } + + private final class ColorSpaceOnItemSelectedListener + implements AdapterView.OnItemSelectedListener { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + ColorBitmapActivity.this.mColorSpace = + getFromName(ColorBitmapActivity.this.mColorNames[position]); + ColorBitmapActivity.this.getMainExecutor() + .execute(ColorBitmapActivity.this::populateBuffers); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + + } + } + + private final class GradientColorOnItemSelectedListener + implements AdapterView.OnItemSelectedListener { + + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + ColorBitmapActivity.this.mGradientEndColor = + ColorBitmapActivity.this.mGradientEndColors[position]; + ColorBitmapActivity.this.getMainExecutor() + .execute(ColorBitmapActivity.this::populateBuffers); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + + } } } |