diff options
236 files changed, 2901 insertions, 1440 deletions
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 538ed423626f..a07735e7540e 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -178,23 +178,6 @@ public class GraphicsEnvironment { } /** - * Query to determine if the Game Mode has enabled ANGLE. - */ - private boolean isAngleEnabledByGameMode(Context context, String packageName) { - try { - final boolean gameModeEnabledAngle = - (mGameManager != null) && mGameManager.isAngleEnabled(packageName); - Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle); - return gameModeEnabledAngle; - } catch (SecurityException e) { - Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled " - + "for package: " + packageName); - } - - return false; - } - - /** * Query to determine the ANGLE driver choice. */ private String queryAngleChoice(Context context, Bundle coreSettings, @@ -422,8 +405,7 @@ public class GraphicsEnvironment { * 2) The per-application switch (i.e. Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS and * Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES; which corresponds to the * “angle_gl_driver_selection_pkgs” and “angle_gl_driver_selection_values” settings); if it - * forces a choice; - * 3) Use ANGLE if isAngleEnabledByGameMode() returns true; + * forces a choice. */ private String queryAngleChoiceInternal(Context context, Bundle bundle, String packageName) { @@ -457,10 +439,6 @@ public class GraphicsEnvironment { Log.v(TAG, " angle_gl_driver_selection_pkgs=" + optInPackages); Log.v(TAG, " angle_gl_driver_selection_values=" + optInValues); - final String gameModeChoice = isAngleEnabledByGameMode(context, packageName) - ? ANGLE_GL_DRIVER_CHOICE_ANGLE - : ANGLE_GL_DRIVER_CHOICE_DEFAULT; - // Make sure we have good settings to use if (optInPackages.size() == 0 || optInPackages.size() != optInValues.size()) { Log.v(TAG, @@ -469,7 +447,7 @@ public class GraphicsEnvironment { + optInPackages.size() + ", " + "number of values: " + optInValues.size()); - return gameModeChoice; + return ANGLE_GL_DRIVER_CHOICE_DEFAULT; } // See if this application is listed in the per-application settings list @@ -477,7 +455,7 @@ public class GraphicsEnvironment { if (pkgIndex < 0) { Log.v(TAG, packageName + " is not listed in per-application setting"); - return gameModeChoice; + return ANGLE_GL_DRIVER_CHOICE_DEFAULT; } mAngleOptInIndex = pkgIndex; @@ -491,11 +469,9 @@ public class GraphicsEnvironment { return ANGLE_GL_DRIVER_CHOICE_ANGLE; } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) { return ANGLE_GL_DRIVER_CHOICE_NATIVE; - } else { - // The user either chose default or an invalid value; go with the default driver or what - // the game mode indicates - return gameModeChoice; } + // The user either chose default or an invalid value; go with the default driver. + return ANGLE_GL_DRIVER_CHOICE_DEFAULT; } /** diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING index dae9b5ea6a43..60622f18fe3b 100644 --- a/core/java/android/os/TEST_MAPPING +++ b/core/java/android/os/TEST_MAPPING @@ -99,13 +99,10 @@ "BatteryStats[^/]*\\.java", "BatteryUsageStats[^/]*\\.java", "PowerComponents\\.java", + "PowerMonitor[^/]*\\.java", "[^/]*BatteryConsumer[^/]*\\.java" ], - "name": "FrameworksServicesTests", - "options": [ - { "include-filter": "com.android.server.power.stats" }, - { "exclude-filter": "com.android.server.power.stats.BatteryStatsTests" } - ] + "name": "PowerStatsTests" }, { "file_patterns": [ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 21ee0e783fbc..c8c3bd3f4ac9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3792,6 +3792,11 @@ public final class ViewRootImpl implements ViewParent, boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw(); boolean cancelAndRedraw = cancelDueToPreDrawListener || (cancelDraw && mDrewOnceForSync); + if (cancelAndRedraw) { + Log.d(mTag, "Cancelling draw." + + " cancelDueToPreDrawListener=" + cancelDueToPreDrawListener + + " cancelDueToSync=" + (cancelDraw && mDrewOnceForSync)); + } if (!cancelAndRedraw) { // A sync was already requested before the WMS requested sync. This means we need to // sync the buffer, regardless if WMS wants to sync the buffer. diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index 534c9de8102c..5ba2f6caac2d 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -80,13 +80,8 @@ interface IWindowOrganizerController { * Finishes a transition. This must be called for all created transitions. * @param transitionToken Which transition to finish * @param t Changes to make before finishing but in the same SF Transaction. Can be null. - * @param callback Called when t is finished applying. - * @return An ID for the sync operation (see {@link #applySyncTransaction}. This will be - * negative if no sync transaction was attached (null t or callback) */ - int finishTransition(in IBinder transitionToken, - in @nullable WindowContainerTransaction t, - in IWindowContainerTransactionCallback callback); + void finishTransition(in IBinder transitionToken, in @nullable WindowContainerTransaction t); /** @return An interface enabling the management of task organizers. */ ITaskOrganizerController getTaskOrganizerController(); diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java index 695d01e92316..2dc2cbca0548 100644 --- a/core/java/android/window/WindowOrganizer.java +++ b/core/java/android/window/WindowOrganizer.java @@ -115,19 +115,15 @@ public class WindowOrganizer { * Finishes a running transition. * @param transitionToken The transition to finish. Can't be null. * @param t A set of window operations to apply before finishing. - * @param callback A sync callback (if provided). See {@link #applySyncTransaction}. - * @return An ID for the sync operation if performed. See {@link #applySyncTransaction}. * * @hide */ @SuppressLint("ExecutorRegistration") @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) - public int finishTransition(@NonNull IBinder transitionToken, - @Nullable WindowContainerTransaction t, - @Nullable WindowContainerTransactionCallback callback) { + public void finishTransition(@NonNull IBinder transitionToken, + @Nullable WindowContainerTransaction t) { try { - return getWindowOrganizerController().finishTransition(transitionToken, t, - callback != null ? callback.mInterface : null); + getWindowOrganizerController().finishTransition(transitionToken, t); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index f19f6c7949d2..2efe44544f37 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -128,7 +128,7 @@ public class AccessibilityShortcutController { DialogStatus.SHOWN, }) /** Denotes the user shortcut type. */ - private @interface DialogStatus { + @interface DialogStatus { int NOT_SHOWN = 0; int SHOWN = 1; } @@ -333,12 +333,35 @@ public class AccessibilityShortcutController { // Avoid non-a11y users accidentally turning shortcut on without reading this carefully. // Put "don't turn on" as the primary action. final AlertDialog alertDialog = mFrameworkObjectProvider.getAlertDialogBuilder( - // Use SystemUI context so we pick up any theme set in a vendor overlay - mFrameworkObjectProvider.getSystemUiContext()) + // Use SystemUI context so we pick up any theme set in a vendor overlay + mFrameworkObjectProvider.getSystemUiContext()) .setTitle(getShortcutWarningTitle(targets)) .setMessage(getShortcutWarningMessage(targets)) .setCancelable(false) - .setNegativeButton(R.string.accessibility_shortcut_on, null) + .setNegativeButton(R.string.accessibility_shortcut_on, + (DialogInterface d, int which) -> { + String targetServices = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId); + String defaultService = mContext.getString( + R.string.config_defaultAccessibilityService); + // If the targetServices is null, means the user enables a + // shortcut for the default service by triggering the volume keys + // shortcut in the SUW instead of intentionally configuring the + // shortcut on UI. + if (targetServices == null && !TextUtils.isEmpty(defaultService)) { + // The defaultService in the string resource could be a shorten + // form like com.google.android.marvin.talkback/.TalkBackService. + // Converts it to the componentName for consistency before saving + // to the Settings. + final ComponentName configDefaultService = + ComponentName.unflattenFromString(defaultService); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + configDefaultService.flattenToString(), + userId); + } + }) .setPositiveButton(R.string.accessibility_shortcut_off, (DialogInterface d, int which) -> { Settings.Secure.putStringForUser(mContext.getContentResolver(), diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING index 60b160ad18c2..d552e0b8c643 100644 --- a/core/java/com/android/internal/os/TEST_MAPPING +++ b/core/java/com/android/internal/os/TEST_MAPPING @@ -38,11 +38,18 @@ ], "name": "FrameworksServicesTests", "options": [ - { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }, - { "include-filter": "com.android.server.power.stats.BatteryStatsTests" } + { "include-filter": "com.android.server.am.BatteryStatsServiceTest" } ] }, { + "file_patterns": [ + "Battery[^/]*\\.java", + "Kernel[^/]*\\.java", + "[^/]*Power[^/]*\\.java" + ], + "name": "PowerStatsTests" + }, + { "name": "FrameworksCoreTests", "options": [ { diff --git a/core/java/com/android/internal/power/TEST_MAPPING b/core/java/com/android/internal/power/TEST_MAPPING index c6cab183d970..1946f5cc99eb 100644 --- a/core/java/com/android/internal/power/TEST_MAPPING +++ b/core/java/com/android/internal/power/TEST_MAPPING @@ -8,11 +8,7 @@ ] }, { - "name": "FrameworksServicesTests", - "options": [ - { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }, - { "include-filter": "com.android.server.power.stats.BatteryStatsTests" } - ] + "name": "PowerStatsTests" } ] } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 4cf17b78f489..199854818989 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -523,13 +523,14 @@ android_media_AudioSystem_dyn_policy_callback(int event, String8 regId, int val) } jclass clazz = env->FindClass(kClassPathName); - const char* zechars = regId.string(); - jstring zestring = env->NewStringUTF(zechars); + const char *regIdString = regId.string(); + jstring regIdJString = env->NewStringUTF(regIdString); env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative, - event, zestring, val); + event, regIdJString, val); - env->ReleaseStringUTFChars(zestring, zechars); + const char *regIdJChars = env->GetStringUTFChars(regIdJString, NULL); + env->ReleaseStringUTFChars(regIdJString, regIdJChars); env->DeleteLocalRef(clazz); } diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index c7aaeb0339bd..c14da299c6ef 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -91,7 +91,6 @@ android_test { java_resources: [":ApkVerityTestCertDer"], data: [ - ":BstatsTestApp", ":BinderDeathRecipientHelperApp1", ":BinderDeathRecipientHelperApp2", ":com.android.cts.helpers.aosp", diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index 3e4c47b36ed9..05b309b2cd52 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -20,7 +20,6 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="FrameworksCoreTests.apk" /> - <option name="test-file-name" value="BstatsTestApp.apk" /> <option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" /> <option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" /> </target_preparer> diff --git a/core/tests/coretests/BstatsTestApp/OWNERS b/core/tests/coretests/BstatsTestApp/OWNERS deleted file mode 100644 index 4068e2bc03b7..000000000000 --- a/core/tests/coretests/BstatsTestApp/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /BATTERY_STATS_OWNERS diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index 2de1230cf706..cd5ec851e9eb 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -27,9 +27,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import static org.mockito.AdditionalMatchers.aryEq; @@ -68,7 +66,6 @@ import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.speech.tts.Voice; import android.test.mock.MockContentResolver; -import android.text.TextUtils; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -76,7 +73,7 @@ import android.view.accessibility.IAccessibilityManager; import android.widget.Toast; import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider; @@ -232,7 +229,7 @@ public class AccessibilityShortcutControllerTest { throws Exception { configureNoShortcutService(); configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - assertFalse(getController().isAccessibilityShortcutAvailable(false)); + assertThat(getController().isAccessibilityShortcutAvailable(false)).isFalse(); } @Test @@ -240,7 +237,7 @@ public class AccessibilityShortcutControllerTest { throws Exception { configureValidShortcutService(); configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - assertTrue(getController().isAccessibilityShortcutAvailable(false)); + assertThat(getController().isAccessibilityShortcutAvailable(false)).isTrue(); } @Test @@ -248,7 +245,7 @@ public class AccessibilityShortcutControllerTest { throws Exception { configureValidShortcutService(); configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - assertFalse(getController().isAccessibilityShortcutAvailable(true)); + assertThat(getController().isAccessibilityShortcutAvailable(true)).isFalse(); } @Test @@ -256,7 +253,7 @@ public class AccessibilityShortcutControllerTest { throws Exception { configureValidShortcutService(); configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); - assertTrue(getController().isAccessibilityShortcutAvailable(true)); + assertThat(getController().isAccessibilityShortcutAvailable(true)).isTrue(); } @Test @@ -267,10 +264,14 @@ public class AccessibilityShortcutControllerTest { configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); Settings.Secure.putString( mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, null); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); - assertFalse(getController().isAccessibilityShortcutAvailable(true)); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); - assertTrue(getController().isAccessibilityShortcutAvailable(true)); + Settings.Secure.putInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); + assertThat(getController().isAccessibilityShortcutAvailable(true)).isFalse(); + Settings.Secure.putInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.SHOWN); + assertThat(getController().isAccessibilityShortcutAvailable(true)).isTrue(); } @Test @@ -281,7 +282,9 @@ public class AccessibilityShortcutControllerTest { AccessibilityShortcutController accessibilityShortcutController = getController(); configureNoShortcutService(); accessibilityShortcutController.onSettingsChanged(); - assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); + assertThat( + accessibilityShortcutController.isAccessibilityShortcutAvailable(false) + ).isFalse(); } @Test @@ -292,7 +295,9 @@ public class AccessibilityShortcutControllerTest { AccessibilityShortcutController accessibilityShortcutController = getController(); configureValidShortcutService(); accessibilityShortcutController.onSettingsChanged(); - assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); + assertThat( + accessibilityShortcutController.isAccessibilityShortcutAvailable(false) + ).isTrue(); } @Test @@ -302,7 +307,9 @@ public class AccessibilityShortcutControllerTest { AccessibilityShortcutController accessibilityShortcutController = getController(); configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); accessibilityShortcutController.onSettingsChanged(); - assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); + assertThat( + accessibilityShortcutController.isAccessibilityShortcutAvailable(false) + ).isTrue(); } @Test @@ -313,7 +320,9 @@ public class AccessibilityShortcutControllerTest { AccessibilityShortcutController accessibilityShortcutController = getController(); configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); accessibilityShortcutController.onSettingsChanged(); - assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(true)); + assertThat( + accessibilityShortcutController.isAccessibilityShortcutAvailable(true) + ).isFalse(); } @Test @@ -324,7 +333,9 @@ public class AccessibilityShortcutControllerTest { AccessibilityShortcutController accessibilityShortcutController = getController(); configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); accessibilityShortcutController.onSettingsChanged(); - assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable(true)); + assertThat( + accessibilityShortcutController.isAccessibilityShortcutAvailable(true) + ).isTrue(); } @Test @@ -341,11 +352,15 @@ public class AccessibilityShortcutControllerTest { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); AccessibilityShortcutController accessibilityShortcutController = getController(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); accessibilityShortcutController.performAccessibilityShortcut(); - assertEquals(1, Settings.Secure.getInt( - mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0)); + assertThat(Settings.Secure.getInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN)).isEqualTo( + AccessibilityShortcutController.DialogStatus.SHOWN); verify(mResources).getString( R.string.accessibility_shortcut_single_service_warning_title, PACKAGE_NAME_STRING); verify(mAlertDialog).show(); @@ -357,11 +372,12 @@ public class AccessibilityShortcutControllerTest { @Test public void testOnAccessibilityShortcut_withDialogShowing_callsServer() - throws Exception { + throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); AccessibilityShortcutController accessibilityShortcutController = getController(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); accessibilityShortcutController.performAccessibilityShortcut(); accessibilityShortcutController.performAccessibilityShortcut(); verify(mToast).show(); @@ -374,11 +390,12 @@ public class AccessibilityShortcutControllerTest { @Test public void testOnAccessibilityShortcut_ifCanceledFirstTime_showsWarningDialog() - throws Exception { + throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); AccessibilityShortcutController accessibilityShortcutController = getController(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); accessibilityShortcutController.performAccessibilityShortcut(); ArgumentCaptor<AlertDialog.OnCancelListener> cancelListenerCaptor = ArgumentCaptor.forClass(AlertDialog.OnCancelListener.class); @@ -394,49 +411,98 @@ public class AccessibilityShortcutControllerTest { public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); getController().performAccessibilityShortcut(); ArgumentCaptor<DialogInterface.OnClickListener> captor = ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off), captor.capture()); - // Call the button callback, if one exists - if (captor.getValue() != null) { - captor.getValue().onClick(null, 0); - } - assertTrue(TextUtils.isEmpty( - Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))); - assertEquals(0, Settings.Secure.getInt( - mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)); + captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE); + + assertThat( + Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE) + ).isEmpty(); + assertThat(Settings.Secure.getInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo( + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); } @Test public void testClickingTurnOnButtonInDialog_shouldLeaveShortcutReady() throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); getController().performAccessibilityShortcut(); ArgumentCaptor<DialogInterface.OnClickListener> captor = - ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); + ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.accessibility_shortcut_on), captor.capture()); - // Call the button callback, if one exists - if (captor.getValue() != null) { - captor.getValue().onClick(null, 0); - } - assertEquals(SERVICE_NAME_STRING, - Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); - assertEquals(1, Settings.Secure.getInt( - mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)); + captor.getValue().onClick(null, DialogInterface.BUTTON_NEGATIVE); + + assertThat( + Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE) + ).isEqualTo(SERVICE_NAME_STRING); + assertThat(Settings.Secure.getInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo( + AccessibilityShortcutController.DialogStatus.SHOWN); + } + + @Test + public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn() + throws Exception { + configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); + configureDefaultAccessibilityService(); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); + getController().performAccessibilityShortcut(); + + ArgumentCaptor<DialogInterface.OnClickListener> captor = + ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); + verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.accessibility_shortcut_on), + captor.capture()); + captor.getValue().onClick(null, DialogInterface.BUTTON_NEGATIVE); + + assertThat( + Settings.Secure.getString(mContentResolver, + ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING); + assertThat(Settings.Secure.getInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo( + AccessibilityShortcutController.DialogStatus.SHOWN); + } + + @Test + public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff() + throws Exception { + configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); + configureDefaultAccessibilityService(); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); + getController().performAccessibilityShortcut(); + + ArgumentCaptor<DialogInterface.OnClickListener> captor = + ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); + verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off), + captor.capture()); + captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE); + + assertThat( + Settings.Secure.getString(mContentResolver, + ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEmpty(); + assertThat(Settings.Secure.getInt( + mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo( + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); } @Test public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.SHOWN); getController().performAccessibilityShortcut(); verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog); @@ -464,10 +530,10 @@ public class AccessibilityShortcutControllerTest { frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); - assertTrue(frameworkFeatureMap.containsKey(COLOR_INVERSION_COMPONENT_NAME)); - assertTrue(frameworkFeatureMap.containsKey(DALTONIZER_COMPONENT_NAME)); - assertTrue(frameworkFeatureMap.containsKey(REDUCE_BRIGHT_COLORS_COMPONENT_NAME)); - assertTrue(frameworkFeatureMap.containsKey(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME)); + assertThat(frameworkFeatureMap).containsKey(COLOR_INVERSION_COMPONENT_NAME); + assertThat(frameworkFeatureMap).containsKey(DALTONIZER_COMPONENT_NAME); + assertThat(frameworkFeatureMap).containsKey(REDUCE_BRIGHT_COLORS_COMPONENT_NAME); + assertThat(frameworkFeatureMap).containsKey(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME); } @Test @@ -478,7 +544,7 @@ public class AccessibilityShortcutControllerTest { frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); - assertTrue(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME)); + assertThat(frameworkFeatureMap).containsKey(ONE_HANDED_COMPONENT_NAME); } @Test @@ -489,7 +555,7 @@ public class AccessibilityShortcutControllerTest { frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); - assertFalse(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME)); + assertThat(frameworkFeatureMap).doesNotContainKey(ONE_HANDED_COMPONENT_NAME); } @Test @@ -498,7 +564,8 @@ public class AccessibilityShortcutControllerTest { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureValidShortcutService(); when(mServiceInfo.loadSummary(any())).thenReturn(null); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.SHOWN); getController().performAccessibilityShortcut(); verify(mAccessibilityManagerService).performAccessibilityShortcut(null); } @@ -508,7 +575,8 @@ public class AccessibilityShortcutControllerTest { throws Exception { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); configureFirstFrameworkFeature(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.SHOWN); getController().performAccessibilityShortcut(); verifyZeroInteractions(mToast); @@ -523,7 +591,8 @@ public class AccessibilityShortcutControllerTest { configureApplicationTargetSdkVersion(Build.VERSION_CODES.R); configureRequestAccessibilityButton(); configureEnabledService(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.SHOWN); getController().performAccessibilityShortcut(); verifyZeroInteractions(mToast); @@ -538,7 +607,8 @@ public class AccessibilityShortcutControllerTest { configureTtsSpokenPromptEnabled(); configureHandlerCallbackInvocation(); AccessibilityShortcutController accessibilityShortcutController = getController(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); accessibilityShortcutController.performAccessibilityShortcut(); verify(mAlertDialog).show(); @@ -563,7 +633,8 @@ public class AccessibilityShortcutControllerTest { configureTtsSpokenPromptEnabled(); configureHandlerCallbackInvocation(); AccessibilityShortcutController accessibilityShortcutController = getController(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); accessibilityShortcutController.performAccessibilityShortcut(); verify(mAlertDialog).show(); @@ -583,7 +654,8 @@ public class AccessibilityShortcutControllerTest { configureTtsSpokenPromptEnabled(); configureHandlerCallbackInvocation(); AccessibilityShortcutController accessibilityShortcutController = getController(); - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + AccessibilityShortcutController.DialogStatus.NOT_SHOWN); Set<String> features = new HashSet<>(); features.add(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED); doReturn(features, Collections.emptySet()).when(mVoice).getFeatures(); @@ -682,4 +754,13 @@ public class AccessibilityShortcutControllerTest { accessibilityShortcutController.mFrameworkObjectProvider = mFrameworkObjectProvider; return accessibilityShortcutController; } + + private void configureDefaultAccessibilityService() throws Exception { + when(mAccessibilityManagerService + .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) + .thenReturn(Collections.singletonList(SERVICE_NAME_STRING)); + + when(mResources.getString(R.string.config_defaultAccessibilityService)).thenReturn( + SERVICE_NAME_STRING); + } } diff --git a/core/tests/coretests/src/com/android/internal/content/OWNERS b/core/tests/coretests/src/com/android/internal/content/OWNERS index dd9ede53239c..4bb83438b193 100644 --- a/core/tests/coretests/src/com/android/internal/content/OWNERS +++ b/core/tests/coretests/src/com/android/internal/content/OWNERS @@ -1,4 +1,2 @@ -per-file PackageMonitorTest.java = file:/core/java/android/content/pm/OWNERS - per-file Overlay* = file:/core/java/android/content/res/OWNERS diff --git a/core/tests/packagemonitortests/Android.bp b/core/tests/packagemonitortests/Android.bp new file mode 100644 index 000000000000..453b476edbc0 --- /dev/null +++ b/core/tests/packagemonitortests/Android.bp @@ -0,0 +1,41 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "FrameworksCorePackageMonitorTests", + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.runner", + "compatibility-device-util-axt", + "frameworks-base-testutils", + "mockito-target-minus-junit4", + "truth-prebuilt", + ], + libs: ["android.test.runner"], + platform_apis: true, + certificate: "platform", + test_suites: ["device-tests"], + data: [ + ":TestVisibilityApp", + ], +} diff --git a/core/tests/packagemonitortests/AndroidManifest.xml b/core/tests/packagemonitortests/AndroidManifest.xml new file mode 100644 index 000000000000..500a3e192077 --- /dev/null +++ b/core/tests/packagemonitortests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.packagemonitor"> + + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.packagemonitor" + android:label="Frameworks PackageMonitor Core Tests" /> +</manifest> diff --git a/core/tests/packagemonitortests/AndroidTest.xml b/core/tests/packagemonitortests/AndroidTest.xml new file mode 100644 index 000000000000..898dc1877b4b --- /dev/null +++ b/core/tests/packagemonitortests/AndroidTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Runs Frameworks Core Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="FrameworksCorePackageMonitorTests.apk" /> + </target_preparer> + + <!-- Create place to store apks --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="mkdir -p /data/local/tmp/contenttests" /> + <option name="teardown-command" value="rm -rf /data/local/tmp/contenttests"/> + </target_preparer> + <!-- Load additional APKs onto device --> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push" value="TestVisibilityApp.apk->/data/local/tmp/contenttests/TestVisibilityApp.apk" /> + <option name="cleanup" value="true" /> + </target_preparer> + + <option name="test-tag" value="FrameworksCorePackageMonitorTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.packagemonitor" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/core/tests/packagemonitortests/OWNERS b/core/tests/packagemonitortests/OWNERS new file mode 100644 index 000000000000..d825dfd7cf00 --- /dev/null +++ b/core/tests/packagemonitortests/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pm/OWNERS diff --git a/core/tests/coretests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java index 7ccbf1d25674..e082c25fa499 100644 --- a/core/tests/coretests/src/com/android/internal/content/PackageMonitorTest.java +++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java @@ -44,10 +44,10 @@ import org.mockito.MockitoAnnotations; */ @RunWith(AndroidJUnit4.class) public class PackageMonitorTest { - private static final String FAKE_PACKAGE_NAME = "com.android.internal.content.fakeapp"; private static final int FAKE_PACKAGE_UID = 123; private static final int FAKE_USER_ID = 0; + private static final int WAIT_CALLBACK_CALLED_IN_MS = 300; @Mock Context mMockContext; diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java new file mode 100644 index 000000000000..a310edceb3b3 --- /dev/null +++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java @@ -0,0 +1,121 @@ +/* + * 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.internal.content; + +import static com.android.compatibility.common.util.ShellUtils.runShellCommand; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * A test to verify PackageMonitor implementation respects the app visibility. + */ +@RunWith(AndroidJUnit4.class) +public class PackageMonitorVisibilityTest { + private static final String TEST_DATA_PATH = "/data/local/tmp/contenttests/"; + private static final String TEAT_APK_PATH = + TEST_DATA_PATH + "TestVisibilityApp.apk"; + private static final String TEAT_APK_PACKAGE_NAME = "com.example.android.testvisibilityapp"; + private static final int WAIT_CALLBACK_CALLED_IN_SECONDS = 1; + @Test + public void testPackageMonitorPackageVisible() throws Exception { + TestVisibilityPackageMonitor testPackageMonitor = new TestVisibilityPackageMonitor(); + + try { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + testPackageMonitor.register(context, UserHandle.ALL, + new Handler(Looper.getMainLooper())); + + installTestPackage(true /* forceQueryable */); + boolean result = testPackageMonitor.mCallbackCountDownLatch.await( + WAIT_CALLBACK_CALLED_IN_SECONDS, TimeUnit.SECONDS); + + int expectedUid = context.getPackageManager().getPackageUid(TEAT_APK_PACKAGE_NAME, + PackageManager.PackageInfoFlags.of(0)); + assertThat(result).isTrue(); + assertThat(testPackageMonitor.mAddedPackageName).isEqualTo(TEAT_APK_PACKAGE_NAME); + assertThat(testPackageMonitor.mAddedPackageUid).isEqualTo(expectedUid); + } finally { + testPackageMonitor.unregister(); + uninstallTestPackage(); + } + } + + @Test + public void testPackageMonitorPackageNotVisible() throws Exception { + TestVisibilityPackageMonitor testPackageMonitor = new TestVisibilityPackageMonitor(); + + try { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + testPackageMonitor.register(context, UserHandle.ALL, + new Handler(Looper.getMainLooper())); + + installTestPackage(false /* forceQueryable */); + boolean result = testPackageMonitor.mCallbackCountDownLatch.await( + WAIT_CALLBACK_CALLED_IN_SECONDS, TimeUnit.SECONDS); + + assertThat(result).isFalse(); + } finally { + testPackageMonitor.unregister(); + uninstallTestPackage(); + } + } + + private static void installTestPackage(boolean forceQueryable) { + final StringBuilder cmd = new StringBuilder("pm install "); + if (forceQueryable) { + cmd.append("--force-queryable "); + } + cmd.append(TEAT_APK_PATH); + final String result = runShellCommand(cmd.toString()); + assertThat(result.trim()).contains("Success"); + } + + private static void uninstallTestPackage() { + runShellCommand("pm uninstall " + TEAT_APK_PACKAGE_NAME); + } + + private static class TestVisibilityPackageMonitor extends PackageMonitor { + String mAddedPackageName; + int mAddedPackageUid; + CountDownLatch mCallbackCountDownLatch = new CountDownLatch(1); + + @Override + public void onPackageAdded(String packageName, int uid) { + if (!TEAT_APK_PACKAGE_NAME.equals(packageName)) { + return; + } + mAddedPackageName = packageName; + mAddedPackageUid = uid; + mCallbackCountDownLatch.countDown(); + } + } +} diff --git a/core/tests/packagemonitortests/testapp/TestVisibilityApp/Android.bp b/core/tests/packagemonitortests/testapp/TestVisibilityApp/Android.bp new file mode 100644 index 000000000000..c0e98fcc595d --- /dev/null +++ b/core/tests/packagemonitortests/testapp/TestVisibilityApp/Android.bp @@ -0,0 +1,27 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "TestVisibilityApp", + srcs: ["src/**/*.java"], + sdk_version: "current", + dex_preopt: { + enabled: false, + }, + test_suites: ["device-tests"], +} diff --git a/core/tests/packagemonitortests/testapp/TestVisibilityApp/AndroidManifest.xml b/core/tests/packagemonitortests/testapp/TestVisibilityApp/AndroidManifest.xml new file mode 100644 index 000000000000..0ba5058be979 --- /dev/null +++ b/core/tests/packagemonitortests/testapp/TestVisibilityApp/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.testvisibilityapp"> + <application android:label="testvisibilityapp"> + <activity android:name="DummyActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/core/tests/packagemonitortests/testapp/TestVisibilityApp/res/layout/dummy_activity.xml b/core/tests/packagemonitortests/testapp/TestVisibilityApp/res/layout/dummy_activity.xml new file mode 100644 index 000000000000..9887dc7117d1 --- /dev/null +++ b/core/tests/packagemonitortests/testapp/TestVisibilityApp/res/layout/dummy_activity.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<EditText xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/text" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:textSize="18sp" + android:autoText="true" + android:capitalize="sentences" + android:text="@string/activity_text_text" /> + diff --git a/core/tests/packagemonitortests/testapp/TestVisibilityApp/res/values/strings.xml b/core/tests/packagemonitortests/testapp/TestVisibilityApp/res/values/strings.xml new file mode 100644 index 000000000000..5771bd50cfe7 --- /dev/null +++ b/core/tests/packagemonitortests/testapp/TestVisibilityApp/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + + <string name="activity_text_text">Hello</string> + +</resources> diff --git a/core/tests/packagemonitortests/testapp/TestVisibilityApp/src/com/example/android/testvisibilityapp/DummyActivity.java b/core/tests/packagemonitortests/testapp/TestVisibilityApp/src/com/example/android/testvisibilityapp/DummyActivity.java new file mode 100644 index 000000000000..e74454e938b9 --- /dev/null +++ b/core/tests/packagemonitortests/testapp/TestVisibilityApp/src/com/example/android/testvisibilityapp/DummyActivity.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.testvisibilityapp; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; + +public class DummyActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + View view = getLayoutInflater().inflate(R.layout.dummy_activity, null); + setContentView(view); + } +} + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java index 57d374b2b8f5..06ce37148eaf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java @@ -186,6 +186,6 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle if (callback == null) { throw new IllegalStateException("No finish callback found"); } - callback.onTransitionFinished(null /* wct */, null /* wctCB */); + callback.onTransitionFinished(null /* wct */); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index e8fa638bea31..d3fada37a685 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -722,10 +722,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return bounds.width() > bounds.height(); } - public boolean isDensityChanged(int densityDpi) { - return mDensity != densityDpi; - } - /** * Return if this layout is landscape. */ 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 16b23935559c..024465b281b8 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 @@ -241,7 +241,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition public void onAnimationEnd(Animator animation) { mDesktopModeWindowDecoration.hideResizeVeil(); mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null, null)); + () -> finishCallback.onTransitionFinished(null)); } }); animator.start(); @@ -271,8 +271,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition startT.apply(); - mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null, null)); + mTransitions.getMainExecutor().execute(() -> finishCallback.onTransitionFinished(null)); return true; } @@ -324,7 +323,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition mOnAnimationFinishedCallback.accept(finishT); } mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null, null)); + () -> finishCallback.onTransitionFinished(null)); } }); @@ -378,7 +377,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition mOnAnimationFinishedCallback.accept(finishT); } mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null, null)); + () -> finishCallback.onTransitionFinished(null)); } }); animator.start(); 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 3ad5edf0e604..7342bd1ae5de 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 @@ -168,7 +168,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH mOnAnimationFinishedCallback.accept(finishT); } mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null, null)); + () -> finishCallback.onTransitionFinished(null)); } }); animator.start(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt index 94788e45e2b6..b9cb5c75aaf0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt @@ -104,7 +104,7 @@ class ToggleResizeDesktopTaskTransitionHandler( .setWindowCrop(leash, endBounds.width(), endBounds.height()) .show(leash) windowDecor.hideResizeVeil() - finishCallback.onTransitionFinished(null, null) + finishCallback.onTransitionFinished(null) boundsAnimator = null } ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java index 55e34fe3d836..84027753435b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java @@ -130,7 +130,7 @@ public class FreeformTaskTransitionHandler if (!animations.isEmpty()) return; mMainExecutor.execute(() -> { mAnimations.remove(transition); - finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + finishCallback.onTransitionFinished(null /* wct */); }); }; for (TransitionInfo.Change change : info.getChanges()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index 56bd188a0d7d..2ef92adb90f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -19,7 +19,6 @@ package com.android.wm.shell.keyguard; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING; import static android.view.WindowManager.TRANSIT_SLEEP; @@ -169,7 +168,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler // Post our finish callback to let startAnimation finish first. mMainExecutor.executeDelayed(() -> { mStartedTransitions.remove(transition); - finishCallback.onTransitionFinished(wct, null); + finishCallback.onTransitionFinished(wct); }, 0); } }); @@ -206,7 +205,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler // implementing an AIDL interface. Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e); } - nextFinishCallback.onTransitionFinished(null, null); + nextFinishCallback.onTransitionFinished(null); } else if (nextInfo.getType() == TRANSIT_SLEEP) { // An empty SLEEP transition comes in as a signal to abort transitions whenever a sleep // token is held. In cases where keyguard is showing, we are running the animation for 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 208f9b7432bf..0d55018ba580 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 @@ -592,7 +592,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, SplitScreenController split = mSplitScreenOptional.get(); if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) { split.prepareExitSplitScreen(wct, split.getStageOfTask( - mTaskInfo.lastParentTaskIdBeforePip)); + mTaskInfo.lastParentTaskIdBeforePip), + SplitScreenController.EXIT_REASON_APP_FINISHED); } } mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds); 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 e3d53fc415db..2563d984b793 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 @@ -447,7 +447,7 @@ public class PipTransition extends PipTransitionController { // handler if there is a pending PiP animation. final Transitions.TransitionFinishCallback finishCallback = mFinishCallback; mFinishCallback = null; - finishCallback.onTransitionFinished(wct, null /* callback */); + finishCallback.onTransitionFinished(wct); } @Override @@ -456,7 +456,7 @@ public class PipTransition extends PipTransitionController { // for example, when app crashes while in PiP and exit transition has not started mCurrentPipTaskToken = null; if (mFinishCallback == null) return; - mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); + mFinishCallback.onTransitionFinished(null /* wct */); mFinishCallback = null; mFinishTransaction = null; } @@ -586,7 +586,7 @@ public class PipTransition extends PipTransitionController { final boolean useLocalLeash = activitySc != null; final boolean toFullscreen = pipChange.getEndAbsBounds().equals( mPipBoundsState.getDisplayBounds()); - mFinishCallback = (wct, wctCB) -> { + mFinishCallback = (wct) -> { mPipOrganizer.onExitPipFinished(taskInfo); // TODO(b/286346098): remove the OPEN app flicker completely @@ -610,7 +610,7 @@ public class PipTransition extends PipTransitionController { mPipAnimationController.resetAnimatorState(); finishTransaction.remove(pipLeash); } - finishCallback.onTransitionFinished(wct, wctCB); + finishCallback.onTransitionFinished(wct); }; mFinishTransaction = finishTransaction; @@ -750,7 +750,7 @@ public class PipTransition extends PipTransitionController { finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(), mPipDisplayLayoutState.getDisplayBounds()); mPipOrganizer.onExitPipFinished(taskInfo); - finishCallback.onTransitionFinished(null, null); + finishCallback.onTransitionFinished(null); } /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */ @@ -1045,7 +1045,7 @@ public class PipTransition extends PipTransitionController { startTransaction.apply(); mPipOrganizer.onExitPipFinished(taskInfo); - finishCallback.onTransitionFinished(null, null); + finishCallback.onTransitionFinished(null); } private void resetPrevPip(@NonNull TransitionInfo.Change prevPipTaskChange, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 7d82dc17ae72..26b7b684e1d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -799,6 +799,14 @@ public class PipController implements PipTransitionController.PipTransitionCallb } private void onDisplayChangedUncheck(DisplayLayout layout, boolean saveRestoreSnapFraction) { + if (mPipTransitionState.getInSwipePipToHomeTransition()) { + // If orientation is changed when performing swipe-pip animation, DisplayLayout has + // been updated in startSwipePipToHome. So it is unnecessary to update again when + // receiving onDisplayConfigurationChanged. This also avoids TouchHandler.userResizeTo + // update surface position in different orientation by the intermediate state. The + // desired resize will be done by the end of transition. + return; + } Runnable updateDisplayLayout = () -> { final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS && mPipDisplayLayoutState.getDisplayLayout().rotation() != layout.rotation(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 88a81fc291b2..a11d9528a170 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -771,7 +771,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { Slog.e(TAG, "Error sending appeared tasks to recents animation", e); } } - finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + finishCallback.onTransitionFinished(null /* wct */); } /** For now, just set-up a jump-cut to the new activity. */ @@ -937,7 +937,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } cleanUp(); - finishCB.onTransitionFinished(wct.isEmpty() ? null : wct, null /* wctCB */); + finishCB.onTransitionFinished(wct.isEmpty() ? null : wct); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 7699b4bfd13a..5fa26542ee07 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -423,8 +423,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, * transition. */ public void prepareExitSplitScreen(WindowContainerTransaction wct, - @StageType int stageToTop) { + @StageType int stageToTop, @ExitReason int reason) { mStageCoordinator.prepareExitSplitScreen(stageToTop, wct); + mStageCoordinator.clearSplitPairedInRecents(reason); } public void enterSplitScreen(int taskId, boolean leftOrTop) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index d21f8a48e62a..99be5b8ee861 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -43,7 +43,6 @@ import android.window.RemoteTransition; import android.window.TransitionInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.TransactionPool; @@ -109,7 +108,7 @@ class SplitScreenTransitions { if (pendingTransition.mCanceled) { // The pending transition was canceled, so skip playing animation. startTransaction.apply(); - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); return; } @@ -211,7 +210,7 @@ class SplitScreenTransitions { } } t.apply(); - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); } /** Play animation for drag divider dismiss transition. */ @@ -238,7 +237,7 @@ class SplitScreenTransitions { mAnimations.remove(va); if (animated) { mTransitions.getMainExecutor().execute(() -> { - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); }); } }); @@ -250,7 +249,7 @@ class SplitScreenTransitions { } } startTransaction.apply(); - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); } /** Play animation for resize transition. */ @@ -283,7 +282,7 @@ class SplitScreenTransitions { mAnimations.remove(va); if (animated) { mTransitions.getMainExecutor().execute(() -> { - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); }); } }); @@ -291,7 +290,7 @@ class SplitScreenTransitions { } startTransaction.apply(); - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); } boolean isPendingTransition(IBinder transition) { @@ -391,7 +390,7 @@ class SplitScreenTransitions { if (mPendingResize != null) { mPendingResize.cancel(null); mAnimations.clear(); - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); } IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler); @@ -450,7 +449,7 @@ class SplitScreenTransitions { } } - void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) { + void onFinish(WindowContainerTransaction wct) { if (!mAnimations.isEmpty()) return; if (wct == null) wct = new WindowContainerTransaction(); @@ -470,7 +469,7 @@ class SplitScreenTransitions { mOnFinish.run(); if (mFinishCallback != null) { - mFinishCallback.onTransitionFinished(wct /* wct */, wctCB /* wctCB */); + mFinishCallback.onTransitionFinished(wct /* wct */); mFinishCallback = null; } } @@ -495,7 +494,7 @@ class SplitScreenTransitions { mTransactionPool.release(transaction); mTransitions.getMainExecutor().execute(() -> { mAnimations.remove(va); - onFinish(null /* wct */, null /* wctCB */); + onFinish(null /* wct */); }); } }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 7d62f58014f0..69609ac6b752 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -83,7 +83,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; -import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; @@ -1494,7 +1493,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void clearSplitPairedInRecents(@ExitReason int exitReason) { + void clearSplitPairedInRecents(@ExitReason int exitReason) { if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return; mRecentTasks.ifPresent(recentTasks -> { @@ -1779,8 +1778,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRootTaskInfo = taskInfo; if (mSplitLayout != null && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) - && mMainStage.isActive() - && !ENABLE_SHELL_TRANSITIONS) { + && mMainStage.isActive()) { // Clear the divider remote animating flag as the divider will be re-rendered to apply // the new rotation config. mIsDividerRemoteAnimating = false; @@ -2218,20 +2216,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDisplayController.addDisplayChangingController(this::onDisplayChange); } - @Override - public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - if (mSplitLayout != null && mSplitLayout.isDensityChanged(newConfig.densityDpi) - && mMainStage.isActive() - && mSplitLayout.updateConfiguration(newConfig) - && ENABLE_SHELL_TRANSITIONS) { - mSplitLayout.update(null /* t */); - onLayoutSizeChanged(mSplitLayout); - } - } - /** * Update surfaces of the split screen layout based on the current state * @param transaction to write the updates to diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index a743e99d6954..adae21b20b3c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -94,9 +94,11 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { mShellExecutor = organizer.getExecutor(); mSyncQueue = syncQueue; mTaskViewTransitions = taskViewTransitions; - if (mTaskViewTransitions != null) { - mTaskViewTransitions.addTaskView(this); - } + mShellExecutor.execute(() -> { + if (mTaskViewTransitions != null) { + mTaskViewTransitions.addTaskView(this); + } + }); mGuard.open("release"); } @@ -225,10 +227,10 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { } private void performRelease() { - if (mTaskViewTransitions != null) { - mTaskViewTransitions.removeTaskView(this); - } mShellExecutor.execute(() -> { + if (mTaskViewTransitions != null) { + mTaskViewTransitions.removeTaskView(this); + } mTaskOrganizer.removeListener(this); resetTaskInfo(); }); @@ -410,9 +412,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { if (mTaskToken == null) { return; } - // Sync Transactions can't operate simultaneously with shell transition collection. + if (isUsingShellTransitions()) { - mTaskViewTransitions.setTaskBounds(this, boundsOnScreen); + mShellExecutor.execute(() -> { + // Sync Transactions can't operate simultaneously with shell transition collection. + mTaskViewTransitions.setTaskBounds(this, boundsOnScreen); + }); return; } @@ -430,9 +435,11 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { Slog.w(TAG, "Trying to remove a task that was never added? (no taskToken)"); return; } - WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.removeTask(mTaskToken); - mTaskViewTransitions.closeTaskView(wct, this); + mShellExecutor.execute(() -> { + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.removeTask(mTaskToken); + mTaskViewTransitions.closeTaskView(wct, this); + }); } /** 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 daf8be60651a..e03f82526bdb 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 @@ -390,7 +390,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { } // No animation, just show it immediately. startTransaction.apply(); - finishCallback.onTransitionFinished(wct, null /* wctCB */); + finishCallback.onTransitionFinished(wct); startNextTransition(); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 052af3af98cc..986560bd6053 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -40,7 +40,6 @@ import android.view.WindowManager; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; -import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.split.SplitScreenUtils; @@ -124,14 +123,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mTransition = transition; } - void joinFinishArgs(WindowContainerTransaction wct, - WindowContainerTransactionCallback wctCB) { - if (wctCB != null) { - // Technically can probably support 1, but don't want to encourage CB usage since - // it creates instabliity, so just throw. - throw new IllegalArgumentException("Can't mix transitions that require finish" - + " sync callback"); - } + void joinFinishArgs(WindowContainerTransaction wct) { if (wct != null) { if (mFinishWCT == null) { mFinishWCT = wct; @@ -389,12 +381,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, info.getChanges().remove(i); } } - Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + Transitions.TransitionFinishCallback finishCB = (wct) -> { --mixed.mInFlightSubAnimations; - mixed.joinFinishArgs(wct, wctCB); + mixed.joinFinishArgs(wct); if (mixed.mInFlightSubAnimations > 0) return; mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB); + finishCallback.onTransitionFinished(mixed.mFinishWCT); }; if (pipChange == null) { if (mixed.mLeftoversHandler != null) { @@ -461,15 +453,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return false; } final boolean isGoingHome = homeIsOpening; - Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + Transitions.TransitionFinishCallback finishCB = (wct) -> { --mixed.mInFlightSubAnimations; - mixed.joinFinishArgs(wct, wctCB); + mixed.joinFinishArgs(wct); if (mixed.mInFlightSubAnimations > 0) return; mActiveTransitions.remove(mixed); if (isGoingHome) { mSplitHandler.onTransitionAnimationComplete(); } - finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB); + finishCallback.onTransitionFinished(mixed.mFinishWCT); }; if (isGoingHome || mSplitHandler.getSplitItemPosition(pipChange.getLastParent()) != SPLIT_POSITION_UNDEFINED) { @@ -586,12 +578,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, // We need to split the transition into 2 parts: the split part and the display part. mixed.mInFlightSubAnimations = 2; - Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + Transitions.TransitionFinishCallback finishCB = (wct) -> { --mixed.mInFlightSubAnimations; - mixed.joinFinishArgs(wct, wctCB); + mixed.joinFinishArgs(wct); if (mixed.mInFlightSubAnimations > 0) return; mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(mixed.mFinishWCT, null /* wctCB */); + finishCallback.onTransitionFinished(mixed.mFinishWCT); }; // Dispatch the display change. This will most-likely be taken by the default handler. @@ -614,7 +606,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull Transitions.TransitionFinishCallback finishCallback) { // Split-screen is only interested in the recents transition finishing (and merging), so // just wrap finish and start recents animation directly. - Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + Transitions.TransitionFinishCallback finishCB = (wct) -> { mixed.mInFlightSubAnimations = 0; mActiveTransitions.remove(mixed); // If pair-to-pair switching, the post-recents clean-up isn't needed. @@ -626,7 +618,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mSplitHandler.onRecentsPairToPairAnimationFinish(wct); } mSplitHandler.onTransitionAnimationComplete(); - finishCallback.onTransitionFinished(wct, wctCB); + finishCallback.onTransitionFinished(wct); }; mixed.mInFlightSubAnimations = 1; mSplitHandler.onRecentsInSplitAnimationStart(info); @@ -644,11 +636,11 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - final Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + final Transitions.TransitionFinishCallback finishCB = (wct) -> { mixed.mInFlightSubAnimations--; if (mixed.mInFlightSubAnimations == 0) { mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(wct, wctCB); + finishCallback.onTransitionFinished(wct); } }; mixed.mInFlightSubAnimations++; @@ -693,11 +685,11 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - final Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + final Transitions.TransitionFinishCallback finishCB = (wct) -> { mixed.mInFlightSubAnimations--; if (mixed.mInFlightSubAnimations > 0) return; mActiveTransitions.remove(mixed); - finishCallback.onTransitionFinished(wct, wctCB); + finishCallback.onTransitionFinished(wct); }; mixed.mInFlightSubAnimations = 1; // Sync pip state. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index dc78c9b139f9..7df658e6c9db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -300,7 +300,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // immediately finishes since there is no animation for screen-wake. if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) { startTransaction.apply(); - finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + finishCallback.onTransitionFinished(null /* wct */); return true; } @@ -309,7 +309,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { || (info.getFlags() & WindowManager.TRANSIT_FLAG_INVISIBLE) != 0) { startTransaction.apply(); finishTransaction.apply(); - finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + finishCallback.onTransitionFinished(null /* wct */); return true; } @@ -323,7 +323,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; mAnimations.remove(transition); - finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + finishCallback.onTransitionFinished(null /* wct */); }; final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index 4e3d220f1ea2..fab2dd2bf3e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -68,7 +68,7 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { final IBinder.DeathRecipient remoteDied = () -> { Log.e(Transitions.TAG, "Remote transition died, finishing"); mMainExecutor.execute( - () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */)); + () -> finishCallback.onTransitionFinished(null /* wct */)); }; IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { @Override @@ -81,7 +81,7 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { finishTransaction.merge(sct); } mMainExecutor.execute(() -> { - finishCallback.onTransitionFinished(wct, null /* wctCB */); + finishCallback.onTransitionFinished(wct); }); } }; @@ -104,7 +104,7 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { if (mRemote.asBinder() != null) { mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */); } - finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + finishCallback.onTransitionFinished(null /* wct */); } return true; } @@ -122,8 +122,7 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { // remote applied the transaction, but applying twice will break surfaceflinger // so just assume the worst-case and clear the local transaction. t.clear(); - mMainExecutor.execute( - () -> finishCallback.onTransitionFinished(wct, null /* wctCB */)); + mMainExecutor.execute(() -> finishCallback.onTransitionFinished(wct)); } }; try { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index c22cc6fbea8f..bbf67a6155d7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -133,7 +133,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { } mMainExecutor.execute(() -> { mRequestedRemotes.remove(transition); - finishCallback.onTransitionFinished(wct, null /* wctCB */); + finishCallback.onTransitionFinished(wct); }); } }; @@ -153,8 +153,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { Log.e(Transitions.TAG, "Error running remote transition.", e); unhandleDeath(remote.asBinder(), finishCallback); mRequestedRemotes.remove(transition); - mMainExecutor.execute( - () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */)); + mMainExecutor.execute(() -> finishCallback.onTransitionFinished(null /* wct */)); } return true; } @@ -210,7 +209,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { + "that the mergeTarget's RemoteTransition impl erroneously " + "accepted/ran the merge request after finishing the mergeTarget"); } - finishCallback.onTransitionFinished(wct, null /* wctCB */); + finishCallback.onTransitionFinished(wct); }); } }; @@ -316,8 +315,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { } } for (int i = mPendingFinishCallbacks.size() - 1; i >= 0; --i) { - mPendingFinishCallbacks.get(i).onTransitionFinished( - null /* wct */, null /* wctCB */); + mPendingFinishCallbacks.get(i).onTransitionFinished(null /* wct */); } mPendingFinishCallbacks.clear(); }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java index d2795959494a..87c438a5b37d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java @@ -43,7 +43,7 @@ class SleepHandler implements Transitions.TransitionHandler { @NonNull Transitions.TransitionFinishCallback finishCallback) { mSleepTransitions.remove(transition); startTransaction.apply(); - finishCallback.onTransitionFinished(null, null); + finishCallback.onTransitionFinished(null); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index e2dce88d5958..b4d0a31dc8c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -62,7 +62,6 @@ import android.window.TransitionInfo; import android.window.TransitionMetrics; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; -import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; import androidx.annotation.BinderThread; @@ -829,7 +828,7 @@ public class Transitions implements RemoteCallable<Transitions>, ready.mStartT.apply(); } // finish now since there's nothing to animate. Calls back into processReadyQueue - onFinish(ready, null, null); + onFinish(ready, null); return; } playTransition(ready); @@ -849,7 +848,7 @@ public class Transitions implements RemoteCallable<Transitions>, + " in case they can be merged", ready, playing); mTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId()); playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT, - playing.mToken, (wct, cb) -> onMerged(playing, ready)); + playing.mToken, (wct) -> onMerged(playing, ready)); } private void onMerged(@NonNull ActiveTransition playing, @NonNull ActiveTransition merged) { @@ -899,7 +898,7 @@ public class Transitions implements RemoteCallable<Transitions>, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s", active.mHandler); boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo, - active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active, wct, cb)); + active.mStartT, active.mFinishT, (wct) -> onFinish(active, wct)); if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler"); mTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler); @@ -908,7 +907,7 @@ public class Transitions implements RemoteCallable<Transitions>, } // Otherwise give every other handler a chance active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT, - active.mFinishT, (wct, cb) -> onFinish(active, wct, cb), active.mHandler); + active.mFinishT, (wct) -> onFinish(active, wct), active.mHandler); } /** @@ -985,8 +984,7 @@ public class Transitions implements RemoteCallable<Transitions>, } private void onFinish(ActiveTransition active, - @Nullable WindowContainerTransaction wct, - @Nullable WindowContainerTransactionCallback wctCB) { + @Nullable WindowContainerTransaction wct) { final Track track = mTracks.get(active.getTrack()); if (track.mActiveTransition != active) { Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or " @@ -1035,11 +1033,11 @@ public class Transitions implements RemoteCallable<Transitions>, // Now perform all the finish callbacks (starting with the playing one and then all the // transitions merged into it). releaseSurfaces(active.mInfo); - mOrganizer.finishTransition(active.mToken, wct, wctCB); + mOrganizer.finishTransition(active.mToken, wct); if (active.mMerged != null) { for (int iM = 0; iM < active.mMerged.size(); ++iM) { ActiveTransition merged = active.mMerged.get(iM); - mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */); + mOrganizer.finishTransition(merged.mToken, null /* wct */); releaseSurfaces(merged.mInfo); } active.mMerged.clear(); @@ -1178,7 +1176,7 @@ public class Transitions implements RemoteCallable<Transitions>, forceFinish.mHandler.onTransitionConsumed( forceFinish.mToken, true /* aborted */, null /* finishTransaction */); } - onFinish(forceFinish, null, null); + onFinish(forceFinish, null); } } if (track.isIdle() || mReadyDuringSync.isEmpty()) { @@ -1198,7 +1196,7 @@ public class Transitions implements RemoteCallable<Transitions>, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge sync %s" + " into %s via a SLEEP proxy", nextSync, playing); playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT, - playing.mToken, (wct, cb) -> {}); + playing.mToken, (wct) -> {}); // it's possible to complete immediately. If that happens, just repeat the signal // loop until we either finish everything or start playing an animation that isn't // finishing immediately. @@ -1226,11 +1224,8 @@ public class Transitions implements RemoteCallable<Transitions>, * The transition must not touch the surfaces after this has been called. * * @param wct A WindowContainerTransaction to run along with the transition clean-up. - * @param wctCB A sync callback that will be run when the transition clean-up is done and - * wct has been applied. */ - void onTransitionFinished(@Nullable WindowContainerTransaction wct, - @Nullable WindowContainerTransactionCallback wctCB); + void onTransitionFinished(@Nullable WindowContainerTransaction wct); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index f148412205bf..2eb6e71456db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -169,7 +169,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene animator.stop(); } - mFinishCallback.onTransitionFinished(null, null); + mFinishCallback.onTransitionFinished(null); mFinishCallback = null; mTransition = null; } @@ -193,7 +193,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene } // Apply changes happening during the unfold animation immediately t.apply(); - finishCallback.onTransitionFinished(null, null); + finishCallback.onTransitionFinished(null); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java index fb05c696af82..c9c58de6e82a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java @@ -168,7 +168,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, startTransaction.apply(); mDesktopWindowDecoration.hideResizeVeil(); mCtrlType = CTRL_TYPE_UNDEFINED; - finishCallback.onTransitionFinished(null, null); + finishCallback.onTransitionFinished(null); return true; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java index 4fca8b46a069..2d9304705738 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java @@ -92,7 +92,7 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim .build(); final Animator animator = mAnimRunner.createAnimator( info, mStartTransaction, mFinishTransaction, - () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */), + () -> mFinishCallback.onTransitionFinished(null /* wct */), new ArrayList()); // The animation should be empty when it is behind starting window. diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java index ab1ccd4599a2..0b2265d4ce9c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java @@ -75,7 +75,7 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase { assertNotNull(mAnimRunner); mAnimSpec = mAnimRunner.mAnimationSpec; assertNotNull(mAnimSpec); - mFinishCallback = (wct, wctCB) -> {}; + mFinishCallback = (wct) -> {}; spyOn(mController); spyOn(mAnimRunner); spyOn(mAnimSpec); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java index ba34f1f74cd3..270dbc49835f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java @@ -217,12 +217,10 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation doReturn(animator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any()); mController.startAnimation(mTransition, info, mStartTransaction, mFinishTransaction, mFinishCallback); - verify(mFinishCallback, never()).onTransitionFinished(any(), any()); + verify(mFinishCallback, never()).onTransitionFinished(any()); mController.mergeAnimation(mTransition, info, new SurfaceControl.Transaction(), - mTransition, - (wct, cb) -> { - }); - verify(mFinishCallback).onTransitionFinished(any(), any()); + mTransition, (wct) -> {}); + verify(mFinishCallback).onTransitionFinished(any()); } @Test @@ -238,9 +236,9 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation mController.startAnimation(mTransition, info, mStartTransaction, mFinishTransaction, mFinishCallback); - verify(mFinishCallback, never()).onTransitionFinished(any(), any()); + verify(mFinishCallback, never()).onTransitionFinished(any()); mController.onAnimationFinished(mTransition); - verify(mFinishCallback).onTransitionFinished(any(), any()); + verify(mFinishCallback).onTransitionFinished(any()); // Should not call finish when the finish has already been called. assertThrows(IllegalStateException.class, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index ff380e92322d..99a1ac663286 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -176,7 +176,7 @@ public class ShellTransitionTests extends ShellTestCase { assertEquals(1, mDefaultHandler.activeCount()); mDefaultHandler.finishAll(); mMainExecutor.flushAll(); - verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); } @Test @@ -299,7 +299,7 @@ public class ShellTransitionTests extends ShellTestCase { assertTrue(remoteCalled[0]); mDefaultHandler.finishAll(); mMainExecutor.flushAll(); - verify(mOrganizer, times(1)).finishTransition(eq(transitToken), eq(remoteFinishWCT), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken), eq(remoteFinishWCT)); } @Test @@ -449,7 +449,7 @@ public class ShellTransitionTests extends ShellTestCase { assertTrue(remoteCalled[0]); mDefaultHandler.finishAll(); mMainExecutor.flushAll(); - verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); } @Test @@ -524,20 +524,20 @@ public class ShellTransitionTests extends ShellTestCase { // default handler doesn't merge by default, so it shouldn't increment active count. assertEquals(1, mDefaultHandler.activeCount()); assertEquals(0, mDefaultHandler.mergeCount()); - verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any()); - verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any()); + verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any()); + verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); mDefaultHandler.finishAll(); mMainExecutor.flushAll(); // first transition finished - verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any()); - verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); + verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); // But now the "queued" transition is running assertEquals(1, mDefaultHandler.activeCount()); mDefaultHandler.finishAll(); mMainExecutor.flushAll(); - verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); } @Test @@ -565,15 +565,15 @@ public class ShellTransitionTests extends ShellTestCase { // it should still only have 1 active, but then show 1 merged assertEquals(1, mDefaultHandler.activeCount()); assertEquals(1, mDefaultHandler.mergeCount()); - verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any()); + verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any()); // We don't tell organizer it is finished yet (since we still want to maintain ordering) - verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any()); + verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); mDefaultHandler.finishAll(); mMainExecutor.flushAll(); // transition + merged all finished. - verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any()); - verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); // Make sure nothing was queued assertEquals(0, mDefaultHandler.activeCount()); } @@ -599,8 +599,7 @@ public class ShellTransitionTests extends ShellTestCase { requestStartTransition(transitions, transitTokenNotReady); mDefaultHandler.setSimulateMerge(true); - mDefaultHandler.mFinishes.get(0).second.onTransitionFinished( - null /* wct */, null /* wctCB */); + mDefaultHandler.mFinishes.get(0).second.onTransitionFinished(null /* wct */); // Make sure that the non-ready transition is not merged. assertEquals(0, mDefaultHandler.mergeCount()); @@ -823,8 +822,8 @@ public class ShellTransitionTests extends ShellTestCase { mDefaultHandler.finishAll(); mMainExecutor.flushAll(); // first transition finished - verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any()); - verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); + verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); // But now the "queued" transition is running assertEquals(1, mDefaultHandler.activeCount()); @@ -835,7 +834,7 @@ public class ShellTransitionTests extends ShellTestCase { mDefaultHandler.finishAll(); mMainExecutor.flushAll(); - verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any()); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); // runnable2 and runnable3 are executed after the second transition finishes because there // are no other active transitions, runnable1 isn't executed again. @@ -1449,13 +1448,13 @@ public class ShellTransitionTests extends ShellTestCase { if (mFinishOnSync && info.getType() == TRANSIT_SLEEP) { for (int i = 0; i < mFinishes.size(); ++i) { if (mFinishes.get(i).first != mergeTarget) continue; - mFinishes.remove(i).second.onTransitionFinished(null, null); + mFinishes.remove(i).second.onTransitionFinished(null); return; } } if (!(mSimulateMerge || mShouldMerge.contains(transition))) return; mMerged.add(transition); - finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + finishCallback.onTransitionFinished(null /* wct */); } @Nullable @@ -1478,7 +1477,7 @@ public class ShellTransitionTests extends ShellTestCase { mFinishes; mFinishes = new ArrayList<>(); for (int i = finishes.size() - 1; i >= 0; --i) { - finishes.get(i).second.onTransitionFinished(null /* wct */, null /* wctCB */); + finishes.get(i).second.onTransitionFinished(null /* wct */); } mShouldMerge.clear(); } @@ -1486,7 +1485,7 @@ public class ShellTransitionTests extends ShellTestCase { void finishOne() { Pair<IBinder, Transitions.TransitionFinishCallback> fin = mFinishes.remove(0); mMerged.clear(); - fin.second.onTransitionFinished(null /* wct */, null /* wctCB */); + fin.second.onTransitionFinished(null /* wct */); } int activeCount() { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 4759689335e9..e8c9d0dbd884 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -6929,6 +6929,114 @@ public class AudioManager { /** * @hide + * Describes an audio device that has not been categorized with a specific + * audio type. + */ + public static final int AUDIO_DEVICE_CATEGORY_UNKNOWN = 0; + + /** + * @hide + * Describes an audio device which is categorized as something different. + */ + public static final int AUDIO_DEVICE_CATEGORY_OTHER = 1; + + /** + * @hide + * Describes an audio device which was categorized as speakers. + */ + public static final int AUDIO_DEVICE_CATEGORY_SPEAKER = 2; + + /** + * @hide + * Describes an audio device which was categorized as headphones. + */ + public static final int AUDIO_DEVICE_CATEGORY_HEADPHONES = 3; + + /** + * @hide + * Describes an audio device which was categorized as car-kit. + */ + public static final int AUDIO_DEVICE_CATEGORY_CARKIT = 4; + + /** + * @hide + * Describes an audio device which was categorized as watch. + */ + public static final int AUDIO_DEVICE_CATEGORY_WATCH = 5; + + /** + * @hide + * Describes an audio device which was categorized as hearing aid. + */ + public static final int AUDIO_DEVICE_CATEGORY_HEARING_AID = 6; + + /** + * @hide + * Describes an audio device which was categorized as receiver. + */ + public static final int AUDIO_DEVICE_CATEGORY_RECEIVER = 7; + + /** @hide */ + @IntDef(flag = false, prefix = "AUDIO_DEVICE_CATEGORY", value = { + AUDIO_DEVICE_CATEGORY_UNKNOWN, + AUDIO_DEVICE_CATEGORY_OTHER, + AUDIO_DEVICE_CATEGORY_SPEAKER, + AUDIO_DEVICE_CATEGORY_HEADPHONES, + AUDIO_DEVICE_CATEGORY_CARKIT, + AUDIO_DEVICE_CATEGORY_WATCH, + AUDIO_DEVICE_CATEGORY_HEARING_AID, + AUDIO_DEVICE_CATEGORY_RECEIVER } + ) + @Retention(RetentionPolicy.SOURCE) + public @interface AudioDeviceCategory {} + + /** @hide */ + public static String audioDeviceCategoryToString(int audioDeviceCategory) { + switch (audioDeviceCategory) { + case AUDIO_DEVICE_CATEGORY_UNKNOWN: return "AUDIO_DEVICE_CATEGORY_UNKNOWN"; + case AUDIO_DEVICE_CATEGORY_OTHER: return "AUDIO_DEVICE_CATEGORY_OTHER"; + case AUDIO_DEVICE_CATEGORY_SPEAKER: return "AUDIO_DEVICE_CATEGORY_SPEAKER"; + case AUDIO_DEVICE_CATEGORY_HEADPHONES: return "AUDIO_DEVICE_CATEGORY_HEADPHONES"; + case AUDIO_DEVICE_CATEGORY_CARKIT: return "AUDIO_DEVICE_CATEGORY_CARKIT"; + case AUDIO_DEVICE_CATEGORY_WATCH: return "AUDIO_DEVICE_CATEGORY_WATCH"; + case AUDIO_DEVICE_CATEGORY_HEARING_AID: return "AUDIO_DEVICE_CATEGORY_HEARING_AID"; + case AUDIO_DEVICE_CATEGORY_RECEIVER: return "AUDIO_DEVICE_CATEGORY_RECEIVER"; + default: + return new StringBuilder("unknown AudioDeviceCategory ").append( + audioDeviceCategory).toString(); + } + } + + /** + * @hide + * Sets the audio device type of a Bluetooth device given its MAC address + */ + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public void setBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle, + @AudioDeviceCategory int btAudioDeviceType) { + try { + getService().setBluetoothAudioDeviceCategory(address, isBle, btAudioDeviceType); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Gets the audio device type of a Bluetooth device given its MAC address + */ + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + @AudioDeviceCategory + public int getBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle) { + try { + return getService().getBluetoothAudioDeviceCategory(address, isBle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide * Sound dose warning at every 100% of dose during integration window */ public static final int CSD_WARNING_DOSE_REACHED_1X = 1; diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 02f765a3dab9..b2466e990b8f 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -324,6 +324,12 @@ interface IAudioService { @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") boolean isCsdEnabled(); + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + oneway void setBluetoothAudioDeviceCategory(in String address, boolean isBle, int deviceType); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + int getBluetoothAudioDeviceCategory(in String address, boolean isBle); + int setHdmiSystemAudioSupported(boolean on); boolean isHdmiSystemAudioSupported(); diff --git a/media/tests/mediatestutils/Android.bp b/media/tests/mediatestutils/Android.bp new file mode 100644 index 000000000000..e50e69ac6f4f --- /dev/null +++ b/media/tests/mediatestutils/Android.bp @@ -0,0 +1,49 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +// TODO audio build defaults +java_library { + name: "mediatestutils_host", + host_supported: true, + srcs: [ + "java/com/android/media/mediatestutils/CancelAllFuturesRule.java", + ], + static_libs: [ + "junit", + ], + visibility: [ + ":__subpackages__", + ], +} + +java_library { + name: "mediatestutils", + static_libs: [ + "mediatestutils_host", + "junit", + ], + visibility: [ + "//cts/tests/tests/media:__subpackages__", + ":__subpackages__", + ], +} + +java_test_host { + name: "mediatestutilshosttests", + srcs: ["javatests/**/*.java"], + static_libs: [ + "mediatestutils_host", + "junit", + "truth", + ], + test_suites: ["general-tests"], + test_options: { + unit_test: true, + }, +} diff --git a/media/tests/mediatestutils/OWNERS b/media/tests/mediatestutils/OWNERS new file mode 100644 index 000000000000..b9eb1f82663f --- /dev/null +++ b/media/tests/mediatestutils/OWNERS @@ -0,0 +1,4 @@ +# Bug component: 48436 +atneya@google.com +jmtrivi@google.com +elaurent@google.com diff --git a/media/tests/mediatestutils/java/com/android/media/mediatestutils/CancelAllFuturesRule.java b/media/tests/mediatestutils/java/com/android/media/mediatestutils/CancelAllFuturesRule.java new file mode 100644 index 000000000000..14e261c40d9f --- /dev/null +++ b/media/tests/mediatestutils/java/com/android/media/mediatestutils/CancelAllFuturesRule.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.media.mediatestutils; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +/** + * + */ +public class CancelAllFuturesRule implements TestRule { + private List<Future> mRegisteredFutures = new ArrayList<>(); + + public <T extends Future<?>> T registerFuture(T future) { + mRegisteredFutures.add(future); + return future; + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + base.evaluate(); + } finally { + mRegisteredFutures.forEach(f -> f.cancel(false /* shouldInterrupt */)); + } + } + }; + } +} diff --git a/media/tests/mediatestutils/javatests/com/android/media/mediatestutils/CancelAllFuturesRuleTest.java b/media/tests/mediatestutils/javatests/com/android/media/mediatestutils/CancelAllFuturesRuleTest.java new file mode 100644 index 000000000000..94fa3d7847eb --- /dev/null +++ b/media/tests/mediatestutils/javatests/com/android/media/mediatestutils/CancelAllFuturesRuleTest.java @@ -0,0 +1,120 @@ +/* + * 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.media.mediatestutils; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.rules.ExpectedException; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + + +@RunWith(JUnit4.class) +public class CancelAllFuturesRuleTest { + + public static class TestException extends Throwable { } + + public static class CheckFutureStatusRule implements TestRule { + private final List<CompletableFuture> mFutures = Arrays.asList(new CompletableFuture<>(), + new CompletableFuture<>()); + + private boolean mCompleted; + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + base.evaluate(); + } finally { + // Intentionally suppresses original exception + if (mCompleted) { + assertThat(mFutures.get(0).isDone()) + .isTrue(); + assertThat(mFutures.get(0).isCancelled()) + .isFalse(); + } else { + assertThat(mFutures.get(0).isCancelled()) + .isTrue(); + } + assertThat(mFutures.get(1).isCancelled()) + .isTrue(); + } + } + }; + } + + Future getFuture(int idx) { + return mFutures.get(idx); + } + + void completeFirstFuture(boolean exceptionally) { + assertThat(mFutures.get(0).complete(null)) + .isTrue(); + mCompleted = true; + } + } + + @Rule(order = 0) + public ExpectedException mExpectedThrownRule = ExpectedException.none(); + + @Rule(order = 1) + public CheckFutureStatusRule mRuleVerifyerRule = new CheckFutureStatusRule(); + + @Rule(order = 2) + public CancelAllFuturesRule mCancelRule = new CancelAllFuturesRule(); + + @Test + public void testRuleCancelsFutures_whenFinishesNormally() { + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(0)); + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(1)); + // return normally + } + + @Test + public void testRuleCancelsFutures_whenFinishesExceptionally() throws TestException { + mExpectedThrownRule.expect(TestException.class); + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(0)); + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(1)); + throw new TestException(); + } + + @Test + public void testRuleDoesNotThrow_whenCompletesNormally() { + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(0)); + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(1)); + mRuleVerifyerRule.completeFirstFuture(false); + } + + @Test + public void testRuleDoesNotThrow_whenCompletesExceptionally() { + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(0)); + mCancelRule.registerFuture(mRuleVerifyerRule.getFuture(1)); + mRuleVerifyerRule.completeFirstFuture(false); + } +} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index b66679af3fe3..2d6cc699fc11 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -16,9 +16,7 @@ */ package com.android.packageinstaller; -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY; -import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.Manifest; @@ -808,8 +806,12 @@ public class PackageInstallerActivity extends AlertActivity { } new Handler(Looper.getMainLooper()).postDelayed(() -> { if (!isDestroyed()) { - startActivity(getIntent().addFlags( - FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP)); + // The start flag (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP) doesn't + // work for the multiple user case, i.e. the caller task user and started + // Activity user are not the same. To avoid having multiple PIAs in the task, + // finish the current PackageInstallerActivity + finish(); + startActivity(getIntent()); } }, 500); diff --git a/packages/SystemUI/compose/core/src/com/android/compose/activity/EdgeToEdgeActivitContent.kt b/packages/SystemUI/compose/core/src/com/android/compose/activity/EdgeToEdgeActivitContent.kt new file mode 100644 index 000000000000..97c8076e910b --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/compose/activity/EdgeToEdgeActivitContent.kt @@ -0,0 +1,61 @@ +/* + * 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.compose.activity + +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.android.compose.rememberSystemUiController +import com.android.compose.theme.PlatformTheme + +/** Scaffolding for an edge-to-edge activity content. */ +@Composable +fun EdgeToEdgeActivityContent( + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + // Make the status and navigation bars transparent, ensuring that the status bar icons are dark + // when the theme is light and vice-versa. + val systemUiController = rememberSystemUiController() + val isDarkTheme = isSystemInDarkTheme() + val useDarkIcons = !isDarkTheme + DisposableEffect(systemUiController, useDarkIcons) { + systemUiController.setSystemBarsColor( + color = Color.Transparent, + darkIcons = useDarkIcons, + ) + onDispose {} + } + + PlatformTheme(isDarkTheme) { + val backgroundColor = MaterialTheme.colorScheme.background + Box(modifier.fillMaxSize().background(backgroundColor)) { + CompositionLocalProvider(LocalContentColor provides contentColorFor(backgroundColor)) { + content() + } + } + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index ca7352ef2501..da48762e1960 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -63,7 +63,7 @@ constructor( .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, - initialValue = destinationScenes(up = viewModel.upDestinationSceneKey.value) + initialValue = destinationScenes(up = null) ) @Composable @@ -77,12 +77,12 @@ constructor( } private fun destinationScenes( - up: SceneKey, + up: SceneKey?, ): Map<UserAction, SceneModel> { - return mapOf( - UserAction.Swipe(Direction.UP) to SceneModel(up), - UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade) - ) + return buildMap { + up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) } + this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade) + } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt index d84e67620177..68f010e1c50d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt @@ -42,13 +42,10 @@ import androidx.compose.runtime.key import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.R import com.android.systemui.compose.modifiers.sysuiResTag @@ -70,15 +67,6 @@ fun PeopleScreen( val priorityTiles by viewModel.priorityTiles.collectAsState() val recentTiles by viewModel.recentTiles.collectAsState() - // Make sure to refresh the tiles/conversations when the lifecycle is resumed, so that it - // updates them when going back to the Activity after leaving it. - val lifecycleOwner = LocalLifecycleOwner.current - LaunchedEffect(lifecycleOwner, viewModel) { - lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { - viewModel.onTileRefreshRequested() - } - } - // Call [onResult] this activity when the ViewModel tells us so. LaunchedEffect(viewModel.result) { viewModel.result.collect { result -> diff --git a/packages/SystemUI/res/layout/log_access_user_consent_dialog_permission.xml b/packages/SystemUI/res/layout/log_access_user_consent_dialog_permission.xml index 89e36ac93387..d647c9b9f2ee 100644 --- a/packages/SystemUI/res/layout/log_access_user_consent_dialog_permission.xml +++ b/packages/SystemUI/res/layout/log_access_user_consent_dialog_permission.xml @@ -16,22 +16,29 @@ ** limitations under the License. */ --> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="380dp" - android:layout_height="match_parent" - android:clipToPadding="false"> - <LinearLayout +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="380dp" + android:layout_height="match_parent" + android:clipToPadding="false" + android:orientation="vertical"> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:orientation="vertical" - android:gravity="center" android:paddingLeft="24dp" android:paddingRight="24dp" android:paddingTop="24dp" - android:paddingBottom="24dp"> + android:paddingBottom="24dp" + android:gravity="center"> - <ImageView + <ImageView android:id="@+id/log_access_image_view" android:layout_width="32dp" android:layout_height="32dp" @@ -41,7 +48,7 @@ tools:layout_editor_absoluteY="35dp" android:gravity="center" /> - <TextView + <TextView android:id="@+id/log_access_dialog_title" android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -51,7 +58,7 @@ android:textColor="?android:attr/textColorPrimary" android:gravity="center" /> - <TextView + <TextView android:id="@+id/log_access_dialog_body" android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -60,42 +67,37 @@ android:textAppearance="@style/PrimaryAllowLogAccess" android:textColor="?android:attr/textColorPrimary" android:gravity="center" /> + </LinearLayout> + </ScrollView> - <Space - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" /> + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingLeft="24dp" + android:paddingRight="24dp" + android:paddingTop="24dp" + android:paddingBottom="24dp"> - <LinearLayout - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <Button - android:id="@+id/log_access_dialog_allow_button" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/log_access_confirmation_allow" - style="?permissionGrantButtonTopStyle" - android:textAppearance="@style/PermissionGrantButtonTextAppearance" - android:layout_marginBottom="5dp" - android:layout_centerHorizontal="true" - android:layout_alignParentTop="true" - android:layout_alignParentBottom="true" - android:clipToOutline="true" - android:gravity="center" /> + <Button + android:id="@+id/log_access_dialog_allow_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/log_access_confirmation_allow" + style="?permissionGrantButtonTopStyle" + android:textAppearance="@style/PermissionGrantButtonTextAppearance" + android:layout_marginBottom="5dp" + android:clipToOutline="true" + android:gravity="center" /> - <Button - android:id="@+id/log_access_dialog_deny_button" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/log_access_confirmation_deny" - style="?permissionGrantButtonBottomStyle" - android:textAppearance="@style/PermissionGrantButtonTextAppearance" - android:layout_centerHorizontal="true" - android:layout_alignParentTop="true" - android:layout_alignParentBottom="true" - android:clipToOutline="true" - android:gravity="center" /> - </LinearLayout> + <Button + android:id="@+id/log_access_dialog_deny_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/log_access_confirmation_deny" + style="?permissionGrantButtonBottomStyle" + android:textAppearance="@style/PermissionGrantButtonTextAppearance" + android:clipToOutline="true" + android:gravity="center" /> </LinearLayout> -</ScrollView> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 15ca9d48c62a..d2cb475ad2b0 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -212,6 +212,7 @@ <item type="id" name="keyguard_indication_text" /> <item type="id" name="keyguard_indication_text_bottom" /> <item type="id" name="nssl_guideline" /> + <item type="id" name="split_shade_guideline" /> <item type="id" name="lock_icon" /> <item type="id" name="lock_icon_bg" /> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 2c224f62dac7..3d48f3cc5359 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -244,24 +244,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS return; } updateAodIcons(); - mStatusArea = mView.findViewById(R.id.keyguard_status_area); - if (mSmartspaceController.isEnabled()) { - View ksv = mView.findViewById(R.id.keyguard_slice_view); - int viewIndex = mStatusArea.indexOfChild(ksv); - ksv.setVisibility(View.GONE); - - // TODO(b/261757708): add content observer for the Settings toggle and add/remove - // weather according to the Settings. - if (mSmartspaceController.isDateWeatherDecoupled()) { - addDateWeatherView(viewIndex); - viewIndex += 1; - } - - addSmartspaceView(viewIndex); - } - mSecureSettings.registerContentObserverForUser( Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, false, /* notifyForDescendants */ @@ -275,13 +259,27 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mShowWeatherObserver, UserHandle.USER_ALL ); - updateDoubleLineClock(); - setDateWeatherVisibility(); - setWeatherVisibility(); mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( mKeyguardUnlockAnimationListener); + + if (mSmartspaceController.isEnabled()) { + View ksv = mView.findViewById(R.id.keyguard_slice_view); + int viewIndex = mStatusArea.indexOfChild(ksv); + ksv.setVisibility(View.GONE); + + mSmartspaceController.removeViewsFromParent(mStatusArea); + addSmartspaceView(); + // TODO(b/261757708): add content observer for the Settings toggle and add/remove + // weather according to the Settings. + if (mSmartspaceController.isDateWeatherDecoupled()) { + addDateWeatherView(); + } + } + + setDateWeatherVisibility(); + setWeatherVisibility(); } int getNotificationIconAreaHeight() { @@ -302,29 +300,22 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS void onLocaleListChanged() { if (mSmartspaceController.isEnabled()) { + mSmartspaceController.removeViewsFromParent(mStatusArea); + addSmartspaceView(); if (mSmartspaceController.isDateWeatherDecoupled()) { mDateWeatherView.removeView(mWeatherView); - int index = mStatusArea.indexOfChild(mDateWeatherView); - if (index >= 0) { - mStatusArea.removeView(mDateWeatherView); - addDateWeatherView(index); - } + addDateWeatherView(); setDateWeatherVisibility(); setWeatherVisibility(); } - int index = mStatusArea.indexOfChild(mSmartspaceView); - if (index >= 0) { - mStatusArea.removeView(mSmartspaceView); - addSmartspaceView(index); - } } } - private void addDateWeatherView(int index) { + private void addDateWeatherView() { mDateWeatherView = (ViewGroup) mSmartspaceController.buildAndConnectDateView(mView); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( MATCH_PARENT, WRAP_CONTENT); - mStatusArea.addView(mDateWeatherView, index, lp); + mStatusArea.addView(mDateWeatherView, 0, lp); int startPadding = getContext().getResources().getDimensionPixelSize( R.dimen.below_clock_padding_start); int endPadding = getContext().getResources().getDimensionPixelSize( @@ -344,11 +335,11 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mWeatherView.setPaddingRelative(0, 0, 4, 0); } - private void addSmartspaceView(int index) { + private void addSmartspaceView() { mSmartspaceView = mSmartspaceController.buildAndConnectView(mView); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( MATCH_PARENT, WRAP_CONTENT); - mStatusArea.addView(mSmartspaceView, index, lp); + mStatusArea.addView(mSmartspaceView, 0, lp); int startPadding = getContext().getResources().getDimensionPixelSize( R.dimen.below_clock_padding_start); int endPadding = getContext().getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 8e92941c79fa..73b4c5f47cde 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -150,7 +150,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV @Override public void onInit() { mKeyguardClockSwitchController.init(); - mDumpManager.registerDumpable(this); + + mDumpManager.registerDumpable(getInstanceName(), this); if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { startCoroutines(EmptyCoroutineContext.INSTANCE); } @@ -190,7 +191,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV * Called in notificationPanelViewController to avoid leak */ public void onDestroy() { - mDumpManager.unregisterDumpable(TAG); + mDumpManager.unregisterDumpable(getInstanceName()); } /** @@ -385,7 +386,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV * Updates the alignment of the KeyguardStatusView and animates the transition if requested. */ public void updateAlignment( - ConstraintLayout notifContainerParent, + ConstraintLayout layout, boolean splitShadeEnabled, boolean shouldBeCentered, boolean animate) { @@ -395,16 +396,23 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } mStatusViewCentered = shouldBeCentered; - if (notifContainerParent == null) { + if (layout == null) { return; } ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(notifContainerParent); - int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline; + constraintSet.clone(layout); + int guideline; + if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { + guideline = R.id.split_shade_guideline; + } else { + guideline = R.id.qs_edge_guideline; + } + + int statusConstraint = shouldBeCentered ? PARENT_ID : guideline; constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END); if (!animate) { - constraintSet.applyTo(notifContainerParent); + constraintSet.applyTo(layout); return; } @@ -447,7 +455,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV // old animation rather than setting up the custom animations. if (clockContainerView == null || clockContainerView.getChildCount() == 0) { transition.addListener(mKeyguardStatusAlignmentTransitionListener); - TransitionManager.beginDelayedTransition(notifContainerParent, transition); + TransitionManager.beginDelayedTransition(layout, transition); } else { View clockView = clockContainerView.getChildAt(0); @@ -481,14 +489,14 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } set.addListener(mKeyguardStatusAlignmentTransitionListener); - TransitionManager.beginDelayedTransition(notifContainerParent, set); + TransitionManager.beginDelayedTransition(layout, set); } } else { transition.addListener(mKeyguardStatusAlignmentTransitionListener); - TransitionManager.beginDelayedTransition(notifContainerParent, transition); + TransitionManager.beginDelayedTransition(layout, transition); } - constraintSet.applyTo(notifContainerParent); + constraintSet.applyTo(layout); } @Override @@ -496,6 +504,10 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mView.dump(pw, args); } + String getInstanceName() { + return TAG + "#" + hashCode(); + } + @VisibleForTesting static class SplitShadeTransitionAdapter extends Transition { private static final String PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft"; diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt new file mode 100644 index 000000000000..6d23b11e5d66 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.authentication.data.model + +/** Enumerates all known authentication methods. */ +sealed class AuthenticationMethodModel( + /** + * Whether the authentication method is considered to be "secure". + * + * "Secure" authentication methods require authentication to unlock the device. Non-secure auth + * methods simply require user dismissal. + */ + open val isSecure: Boolean, +) { + /** There is no authentication method on the device. We shouldn't even show the lock screen. */ + object None : AuthenticationMethodModel(isSecure = false) + + object Pin : AuthenticationMethodModel(isSecure = true) + + object Password : AuthenticationMethodModel(isSecure = true) + + object Pattern : AuthenticationMethodModel(isSecure = true) +} diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index deb3d035d753..8d1fc5d9d558 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -14,15 +14,21 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.authentication.data.repository +import android.app.admin.DevicePolicyManager +import android.content.IntentFilter +import android.os.UserHandle import com.android.internal.widget.LockPatternChecker import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel +import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -37,13 +43,17 @@ import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -54,9 +64,10 @@ interface AuthenticationRepository { * Whether the device is unlocked. * * A device that is not yet unlocked requires unlocking by completing an authentication - * challenge according to the current authentication method. - * - * Note that this state has no real bearing on whether the lockscreen is showing or dismissed. + * challenge according to the current authentication method, unless in cases when the current + * authentication method is not "secure" (for example, None); in such cases, the value of this + * flow will always be `true`, even if the lockscreen is showing and still needs to be dismissed + * by the user to proceed. */ val isUnlocked: StateFlow<Boolean> @@ -86,8 +97,29 @@ interface AuthenticationRepository { val throttling: StateFlow<AuthenticationThrottlingModel> /** + * The currently-configured authentication method. This determines how the authentication + * challenge needs to be completed in order to unlock an otherwise locked device. + * + * Note: there may be other ways to unlock the device that "bypass" the need for this + * authentication challenge (notably, biometrics like fingerprint or face unlock). + * + * Note: by design, this is a [Flow] and not a [StateFlow]; a consumer who wishes to get a + * snapshot of the current authentication method without establishing a collector of the flow + * can do so by invoking [getAuthenticationMethod]. + */ + val authenticationMethod: Flow<AuthenticationMethodModel> + + /** * Returns the currently-configured authentication method. This determines how the - * authentication challenge is completed in order to unlock an otherwise locked device. + * authentication challenge needs to be completed in order to unlock an otherwise locked device. + * + * Note: there may be other ways to unlock the device that "bypass" the need for this + * authentication challenge (notably, biometrics like fingerprint or face unlock). + * + * Note: by design, this is offered as a convenience method alongside [authenticationMethod]. + * The flow should be used for code that wishes to stay up-to-date its logic as the + * authentication changes over time and this method should be used for simple code that only + * needs to check the current value. */ suspend fun getAuthenticationMethod(): AuthenticationMethodModel @@ -141,6 +173,7 @@ constructor( private val userRepository: UserRepository, keyguardRepository: KeyguardRepository, private val lockPatternUtils: LockPatternUtils, + broadcastDispatcher: BroadcastDispatcher, ) : AuthenticationRepository { override val isUnlocked = keyguardRepository.isKeyguardUnlocked @@ -148,7 +181,7 @@ constructor( override suspend fun isLockscreenEnabled(): Boolean { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId - !lockPatternUtils.isLockPatternEnabled(selectedUserId) + !lockPatternUtils.isLockScreenDisabled(selectedUserId) } } @@ -172,18 +205,31 @@ constructor( private val UserRepository.selectedUserId: Int get() = getSelectedUserInfo().id + override val authenticationMethod: Flow<AuthenticationMethodModel> = + userRepository.selectedUserInfo + .map { it.id } + .distinctUntilChanged() + .flatMapLatest { selectedUserId -> + broadcastDispatcher + .broadcastFlow( + filter = + IntentFilter( + DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED + ), + user = UserHandle.of(selectedUserId), + ) + .onStart { emit(Unit) } + .map { selectedUserId } + } + .map { selectedUserId -> + withContext(backgroundDispatcher) { + blockingAuthenticationMethodInternal(selectedUserId) + } + } + override suspend fun getAuthenticationMethod(): AuthenticationMethodModel { return withContext(backgroundDispatcher) { - val selectedUserId = userRepository.selectedUserId - when (getSecurityMode.apply(selectedUserId)) { - KeyguardSecurityModel.SecurityMode.PIN, - KeyguardSecurityModel.SecurityMode.SimPin, - KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Pin - KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password - KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern - KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None - KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!") - } + blockingAuthenticationMethodInternal(userRepository.selectedUserId) } } @@ -301,6 +347,27 @@ constructor( return flow.asStateFlow() } + + /** + * Returns the authentication method for the given user ID. + * + * WARNING: this is actually a blocking IPC/"binder" call that's expensive to do on the main + * thread. We keep it not marked as `suspend` because we want to be able to run this without a + * `runBlocking` which has a ton of performance/blocking problems. + */ + private fun blockingAuthenticationMethodInternal( + userId: Int, + ): AuthenticationMethodModel { + return when (getSecurityMode.apply(userId)) { + KeyguardSecurityModel.SecurityMode.PIN, + KeyguardSecurityModel.SecurityMode.SimPin, + KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Pin + KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password + KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern + KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None + KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!") + } + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index d4371bf30e0e..75192021dab6 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -18,8 +18,10 @@ package com.android.systemui.authentication.domain.interactor import com.android.internal.widget.LockPatternView import com.android.internal.widget.LockscreenCredential +import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel import com.android.systemui.authentication.data.repository.AuthenticationRepository -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -35,8 +37,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow 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 import kotlinx.coroutines.flow.stateIn @@ -56,21 +60,40 @@ constructor( private val clock: SystemClock, ) { /** + * The currently-configured authentication method. This determines how the authentication + * challenge needs to be completed in order to unlock an otherwise locked device. + * + * Note: there may be other ways to unlock the device that "bypass" the need for this + * authentication challenge (notably, biometrics like fingerprint or face unlock). + * + * Note: by design, this is a [Flow] and not a [StateFlow]; a consumer who wishes to get a + * snapshot of the current authentication method without establishing a collector of the flow + * can do so by invoking [getAuthenticationMethod]. + * + * Note: this layer adds the synthetic authentication method of "swipe" which is special. When + * the current authentication method is "swipe", the user does not need to complete any + * authentication challenge to unlock the device; they just need to dismiss the lockscreen to + * get past it. This also means that the value of [isUnlocked] remains `false` even when the + * lockscreen is showing and still needs to be dismissed by the user to proceed. + */ + val authenticationMethod: Flow<DomainLayerAuthenticationMethodModel> = + repository.authenticationMethod.map { rawModel -> rawModel.toDomainLayer() } + + /** * Whether the device is unlocked. * * A device that is not yet unlocked requires unlocking by completing an authentication - * challenge according to the current authentication method. - * - * Note that this state has no real bearing on whether the lock screen is showing or dismissed. + * challenge according to the current authentication method, unless in cases when the current + * authentication method is not "secure" (for example, None and Swipe); in such cases, the value + * of this flow will always be `true`, even if the lockscreen is showing and still needs to be + * dismissed by the user to proceed. */ val isUnlocked: StateFlow<Boolean> = - repository.isUnlocked - .map { isUnlocked -> - if (getAuthenticationMethod() is AuthenticationMethodModel.None) { - true - } else { - isUnlocked - } + combine( + repository.isUnlocked, + authenticationMethod, + ) { isUnlocked, authenticationMethod -> + authenticationMethod is DomainLayerAuthenticationMethodModel.None || isUnlocked } .stateIn( scope = applicationScope, @@ -129,18 +152,24 @@ constructor( /** * Returns the currently-configured authentication method. This determines how the - * authentication challenge is completed in order to unlock an otherwise locked device. + * authentication challenge needs to be completed in order to unlock an otherwise locked device. + * + * Note: there may be other ways to unlock the device that "bypass" the need for this + * authentication challenge (notably, biometrics like fingerprint or face unlock). + * + * Note: by design, this is offered as a convenience method alongside [authenticationMethod]. + * The flow should be used for code that wishes to stay up-to-date its logic as the + * authentication changes over time and this method should be used for simple code that only + * needs to check the current value. + * + * Note: this layer adds the synthetic authentication method of "swipe" which is special. When + * the current authentication method is "swipe", the user does not need to complete any + * authentication challenge to unlock the device; they just need to dismiss the lockscreen to + * get past it. This also means that the value of [isUnlocked] remains `false` even when the + * lockscreen is showing and still needs to be dismissed by the user to proceed. */ - suspend fun getAuthenticationMethod(): AuthenticationMethodModel { - val authMethod = repository.getAuthenticationMethod() - return if ( - authMethod is AuthenticationMethodModel.None && repository.isLockscreenEnabled() - ) { - // We treat "None" as "Swipe" when the lockscreen is enabled. - AuthenticationMethodModel.Swipe - } else { - authMethod - } + suspend fun getAuthenticationMethod(): DomainLayerAuthenticationMethodModel { + return repository.getAuthenticationMethod().toDomainLayer() } /** @@ -270,21 +299,38 @@ constructor( } } - private fun AuthenticationMethodModel.createCredential( + private fun DomainLayerAuthenticationMethodModel.createCredential( input: List<Any> ): LockscreenCredential? { return when (this) { - is AuthenticationMethodModel.Pin -> + is DomainLayerAuthenticationMethodModel.Pin -> LockscreenCredential.createPin(input.joinToString("")) - is AuthenticationMethodModel.Password -> + is DomainLayerAuthenticationMethodModel.Password -> LockscreenCredential.createPassword(input.joinToString("")) - is AuthenticationMethodModel.Pattern -> + is DomainLayerAuthenticationMethodModel.Pattern -> LockscreenCredential.createPattern( input - .map { it as AuthenticationMethodModel.Pattern.PatternCoordinate } + .map { it as AuthenticationPatternCoordinate } .map { LockPatternView.Cell.of(it.y, it.x) } ) else -> null } } + + private suspend fun DataLayerAuthenticationMethodModel.toDomainLayer(): + DomainLayerAuthenticationMethodModel { + return when (this) { + is DataLayerAuthenticationMethodModel.None -> + if (repository.isLockscreenEnabled()) { + DomainLayerAuthenticationMethodModel.Swipe + } else { + DomainLayerAuthenticationMethodModel.None + } + is DataLayerAuthenticationMethodModel.Pin -> DomainLayerAuthenticationMethodModel.Pin + is DataLayerAuthenticationMethodModel.Password -> + DomainLayerAuthenticationMethodModel.Password + is DataLayerAuthenticationMethodModel.Pattern -> + DomainLayerAuthenticationMethodModel.Pattern + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt index 97c6697f10a5..d7e6099a8908 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.authentication.shared.model +package com.android.systemui.authentication.domain.model /** Enumerates all known authentication methods. */ sealed class AuthenticationMethodModel( @@ -36,10 +36,5 @@ sealed class AuthenticationMethodModel( object Password : AuthenticationMethodModel(isSecure = true) - object Pattern : AuthenticationMethodModel(isSecure = true) { - data class PatternCoordinate( - val x: Int, - val y: Int, - ) - } + object Pattern : AuthenticationMethodModel(isSecure = true) } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationPatternCoordinate.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationPatternCoordinate.kt new file mode 100644 index 000000000000..8a3f780b3e54 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationPatternCoordinate.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.authentication.shared.model + +data class AuthenticationPatternCoordinate( + val x: Int, + val y: Int, +) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 063f62e19686..869d0846e0ae 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -186,7 +186,7 @@ constructor( } private fun listenForAlternateBouncerVisibility() { - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true) + alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, "SideFpsController") scope.launch { alternateBouncerInteractor.isVisible.collect { isVisible: Boolean -> if (isVisible) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 15bd73193687..db30a55ea1e1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -83,6 +83,7 @@ constructor( dumpManager, ), UdfpsKeyguardViewControllerAdapter { + private val uniqueIdentifier = this.toString() private val useExpandedOverlay: Boolean = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) private var showingUdfpsBouncer = false @@ -282,7 +283,7 @@ constructor( public override fun onViewAttached() { super.onViewAttached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true) + alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier) val dozeAmount = statusBarStateController.dozeAmount lastDozeAmount = dozeAmount stateListener.onDozeAmountChanged(dozeAmount, dozeAmount) @@ -312,7 +313,7 @@ constructor( override fun onViewDetached() { super.onViewDetached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(false) + alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier) faceDetectRunning = false keyguardStateController.removeCallback(keyguardStateControllerCallback) statusBarStateController.removeCallback(stateListener) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt index 2a9f3eafc776..c9b1624d4d50 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt @@ -46,6 +46,7 @@ open class UdfpsKeyguardViewController( dumpManager, ), UdfpsKeyguardViewControllerAdapter { + private val uniqueIdentifier = this.toString() override val tag: String get() = TAG @@ -55,12 +56,12 @@ open class UdfpsKeyguardViewController( public override fun onViewAttached() { super.onViewAttached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true) + alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier) } public override fun onViewDetached() { super.onViewDetached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(false) + alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier) } override fun shouldPauseAuth(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index e3e9b3a3754a..98ae54b1340e 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -40,6 +40,7 @@ constructor( ) { var receivedDownTouch = false val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible + private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet() /** * Sets the correct bouncer states to show the alternate bouncer if it can show. @@ -69,8 +70,15 @@ constructor( return bouncerRepository.alternateBouncerVisible.value } - fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { - bouncerRepository.setAlternateBouncerUIAvailable(isAvailable) + fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) { + if (isAvailable) { + alternateBouncerUiAvailableFromSource.add(token) + } else { + alternateBouncerUiAvailableFromSource.remove(token) + } + bouncerRepository.setAlternateBouncerUIAvailable( + alternateBouncerUiAvailableFromSource.isNotEmpty() + ) } fun canShowAlternateBouncerForFingerprint(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 8ed964d4af22..ffcae1cacb00 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -14,14 +14,12 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package com.android.systemui.bouncer.domain.interactor import android.content.Context import com.android.systemui.R import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.dagger.SysUISingleton @@ -35,7 +33,6 @@ import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -107,14 +104,6 @@ constructor( } /** - * Returns the currently-configured authentication method. This determines how the - * authentication challenge is completed in order to unlock an otherwise locked device. - */ - suspend fun getAuthenticationMethod(): AuthenticationMethodModel { - return authenticationInteractor.getAuthenticationMethod() - } - - /** * Either shows the bouncer or unlocks the device, if the bouncer doesn't need to be shown. * * @param message An optional message to show to the user in the bouncer. @@ -124,7 +113,9 @@ constructor( ) { applicationScope.launch { if (authenticationInteractor.isAuthenticationRequired()) { - repository.setMessage(message ?: promptMessage(getAuthenticationMethod())) + repository.setMessage( + message ?: promptMessage(authenticationInteractor.getAuthenticationMethod()) + ) sceneInteractor.setCurrentScene( scene = SceneModel(SceneKey.Bouncer), loggingReason = "request to unlock device while authentication required", @@ -143,7 +134,9 @@ constructor( * method. */ fun resetMessage() { - applicationScope.launch { repository.setMessage(promptMessage(getAuthenticationMethod())) } + applicationScope.launch { + repository.setMessage(promptMessage(authenticationInteractor.getAuthenticationMethod())) + } } /** Removes the user-facing message. */ @@ -181,7 +174,7 @@ constructor( loggingReason = "successful authentication", ) } else { - repository.setMessage(errorMessage(getAuthenticationMethod())) + repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod())) } return isAuthenticated diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 68e1a29bc609..5b1998d1e5f6 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -14,25 +14,20 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package com.android.systemui.bouncer.ui.viewmodel import android.content.Context import com.android.systemui.R -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlin.math.ceil import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -50,23 +45,24 @@ class BouncerViewModel constructor( @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, - private val interactor: BouncerInteractor, + private val bouncerInteractor: BouncerInteractor, + private val authenticationInteractor: AuthenticationInteractor, featureFlags: FeatureFlags, ) { private val isInputEnabled: StateFlow<Boolean> = - interactor.isThrottled + bouncerInteractor.isThrottled .map { !it } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = !interactor.isThrottled.value, + initialValue = !bouncerInteractor.isThrottled.value, ) private val pin: PinBouncerViewModel by lazy { PinBouncerViewModel( applicationContext = applicationContext, applicationScope = applicationScope, - interactor = interactor, + interactor = bouncerInteractor, isInputEnabled = isInputEnabled, ) } @@ -74,7 +70,7 @@ constructor( private val password: PasswordBouncerViewModel by lazy { PasswordBouncerViewModel( applicationScope = applicationScope, - interactor = interactor, + interactor = bouncerInteractor, isInputEnabled = isInputEnabled, ) } @@ -83,31 +79,35 @@ constructor( PatternBouncerViewModel( applicationContext = applicationContext, applicationScope = applicationScope, - interactor = interactor, + interactor = bouncerInteractor, isInputEnabled = isInputEnabled, ) } /** View-model for the current UI, based on the current authentication method. */ - private val _authMethod = - MutableSharedFlow<AuthMethodBouncerViewModel?>( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) val authMethod: StateFlow<AuthMethodBouncerViewModel?> = - _authMethod.stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = null, - ) + authenticationInteractor.authenticationMethod + .map { authenticationMethod -> + when (authenticationMethod) { + is AuthenticationMethodModel.Pin -> pin + is AuthenticationMethodModel.Password -> password + is AuthenticationMethodModel.Pattern -> pattern + else -> null + } + } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = null, + ) init { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { applicationScope.launch { - interactor.isThrottled + bouncerInteractor.isThrottled .map { isThrottled -> if (isThrottled) { - when (interactor.getAuthenticationMethod()) { + when (authenticationInteractor.getAuthenticationMethod()) { is AuthenticationMethodModel.Pin -> R.string.kg_too_many_failed_pin_attempts_dialog_message is AuthenticationMethodModel.Password -> @@ -118,8 +118,9 @@ constructor( }?.let { stringResourceId -> applicationContext.getString( stringResourceId, - interactor.throttling.value.failedAttemptCount, - ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), + bouncerInteractor.throttling.value.failedAttemptCount, + ceil(bouncerInteractor.throttling.value.remainingMs / 1000f) + .toInt(), ) } } else { @@ -133,25 +134,14 @@ constructor( } } } - - applicationScope.launch { - _authMethod.subscriptionCount - .pairwise() - .map { (previousCount, currentCount) -> currentCount > previousCount } - .collect { subscriberAdded -> - if (subscriberAdded) { - reloadAuthMethod() - } - } - } } } /** The user-facing message to show in the bouncer. */ val message: StateFlow<MessageViewModel> = combine( - interactor.message, - interactor.isThrottled, + bouncerInteractor.message, + bouncerInteractor.isThrottled, ) { message, isThrottled -> toMessageViewModel(message, isThrottled) } @@ -160,8 +150,8 @@ constructor( started = SharingStarted.WhileSubscribed(), initialValue = toMessageViewModel( - message = interactor.message.value, - isThrottled = interactor.isThrottled.value, + message = bouncerInteractor.message.value, + isThrottled = bouncerInteractor.isThrottled.value, ), ) @@ -197,17 +187,6 @@ constructor( ) } - private suspend fun reloadAuthMethod() { - _authMethod.tryEmit( - when (interactor.getAuthenticationMethod()) { - is AuthenticationMethodModel.Pin -> pin - is AuthenticationMethodModel.Password -> password - is AuthenticationMethodModel.Pattern -> pattern - else -> null - } - ) - } - data class MessageViewModel( val text: String, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index 4be539d9396d..4425f9ffcb5e 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel import android.content.Context import android.util.TypedValue -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import kotlin.math.max import kotlin.math.min @@ -193,8 +193,8 @@ data class PatternDotViewModel( val x: Int, val y: Int, ) { - fun toCoordinate(): AuthenticationMethodModel.Pattern.PatternCoordinate { - return AuthenticationMethodModel.Pattern.PatternCoordinate( + fun toCoordinate(): AuthenticationPatternCoordinate { + return AuthenticationPatternCoordinate( x = x, y = y, ) diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index cc371618ddfe..a30a3e1f855c 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -290,7 +290,8 @@ object Flags { val WALLPAPER_PICKER_PREVIEW_ANIMATION = unreleasedFlag( 244, - "wallpaper_picker_preview_animation" + "wallpaper_picker_preview_animation", + teamfood = true ) // 300 - power menu @@ -369,6 +370,10 @@ object Flags { val WIFI_TRACKER_LIB_FOR_WIFI_ICON = unreleasedFlag(613, "wifi_tracker_lib_for_wifi_icon") + // TODO(b/293863612): Tracking Bug + @JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON = + unreleasedFlag(614, "incompatible_charging_battery_icon") + // 700 - dialer/calls // TODO(b/254512734): Tracking Bug val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip") @@ -716,6 +721,11 @@ object Flags { val BIGPICTURE_NOTIFICATION_LAZY_LOADING = unreleasedFlag(283447257, "bigpicture_notification_lazy_loading") + // TODO(b/292062937): Tracking bug + @JvmField + val NOTIFICATION_CLEARABLE_REFACTOR = + unreleasedFlag(292062937, "notification_clearable_refactor") + // TODO(b/283740863): Tracking Bug @JvmField val ENABLE_NEW_PRIVACY_DIALOG = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 6213265cb66e..dffc19d566ff 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -20,6 +20,8 @@ package com.android.systemui.keyguard import android.content.res.Configuration import android.view.View import android.view.ViewGroup +import com.android.keyguard.KeyguardStatusViewController +import com.android.keyguard.dagger.KeyguardStatusViewComponent import com.android.systemui.CoreStartable import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton @@ -80,6 +82,7 @@ constructor( private val chipbarCoordinator: ChipbarCoordinator, private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener, private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel, + private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory, ) : CoreStartable { private var rootViewHandle: DisposableHandle? = null @@ -88,6 +91,7 @@ constructor( private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null private var settingsPopupMenuHandle: DisposableHandle? = null + private var keyguardStatusViewController: KeyguardStatusViewController? = null override fun start() { bindKeyguardRootView() @@ -96,6 +100,7 @@ constructor( unbindKeyguardBottomArea(notificationPanel) bindIndicationArea() bindLockIconView(notificationPanel) + bindKeyguardStatusView(notificationPanel) setupNotificationStackScrollLayout(notificationPanel) bindLeftShortcut() bindRightShortcut() @@ -117,7 +122,7 @@ constructor( sharedNotificationContainer.addNotificationStackScrollLayout(nssl) SharedNotificationContainerBinder.bind( sharedNotificationContainer, - sharedNotificationContainerViewModel + sharedNotificationContainerViewModel, ) } } @@ -255,4 +260,31 @@ constructor( } } } + + fun bindKeyguardStatusView(legacyParent: ViewGroup) { + // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available. + // Disable one of them + if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { + legacyParent.findViewById<View>(R.id.keyguard_status_view)?.let { + legacyParent.removeView(it) + } + + val keyguardStatusView = keyguardRootView.addStatusView() + val statusViewComponent = keyguardStatusViewComponentFactory.build(keyguardStatusView) + val controller = statusViewComponent.getKeyguardStatusViewController() + controller.init() + keyguardStatusViewController = controller + } else { + keyguardRootView.findViewById<View?>(R.id.keyguard_status_view)?.let { + keyguardRootView.removeView(it) + } + } + } + + /** + * Temporary, to allow NotificationPanelViewController to use the same instance while code is + * migrated: b/288242803 + */ + fun getKeyguardStatusViewController() = keyguardStatusViewController + fun getKeyguardRootView() = keyguardRootView } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index f1b344199516..e35c3699aabf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -199,13 +199,16 @@ interface KeyguardRepository { fun setAnimateDozingTransitions(animate: Boolean) /** Sets the current amount of alpha that should be used for rendering the bottom area. */ - @Deprecated("Deprecated as part of b/278057014") - fun setBottomAreaAlpha(alpha: Float) + @Deprecated("Deprecated as part of b/278057014") fun setBottomAreaAlpha(alpha: Float) /** Sets the current amount of alpha that should be used for rendering the keyguard. */ fun setKeyguardAlpha(alpha: Float) - fun setKeyguardVisibility(statusBarState: Int, goingToFullShade: Boolean, occlusionTransitionRunning: Boolean) + fun setKeyguardVisibility( + statusBarState: Int, + goingToFullShade: Boolean, + occlusionTransitionRunning: Boolean + ) /** * Sets the relative offset of the lock-screen clock from its natural position on the screen. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt deleted file mode 100644 index 278c68d3c55b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.keyguard.domain.interactor - -import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn - -/** Hosts business and application state accessing logic for the lockscreen scene. */ -@SysUISingleton -class LockscreenSceneInteractor -@Inject -constructor( - @Application applicationScope: CoroutineScope, - private val authenticationInteractor: AuthenticationInteractor, - private val bouncerInteractor: BouncerInteractor, -) { - /** Whether the device is currently locked. */ - val isDeviceLocked: StateFlow<Boolean> = - authenticationInteractor.isUnlocked - .map { !it } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = !authenticationInteractor.isUnlocked.value, - ) - - /** Whether it's currently possible to swipe up to dismiss the lockscreen. */ - val isSwipeToDismissEnabled: StateFlow<Boolean> = - authenticationInteractor.isUnlocked - .map { isUnlocked -> - !isUnlocked && - authenticationInteractor.getAuthenticationMethod() is - AuthenticationMethodModel.Swipe - } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - - /** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */ - fun dismissLockscreen() { - bouncerInteractor.showOrUnlockDevice() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt index e60901f922c3..a94874176a34 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt @@ -24,6 +24,7 @@ import android.view.View import android.widget.ImageView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.res.ResourcesCompat +import com.android.keyguard.KeyguardStatusView import com.android.keyguard.LockIconView import com.android.systemui.R import com.android.systemui.animation.view.LaunchableImageView @@ -38,6 +39,8 @@ class KeyguardRootView( attrs, ) { + private var statusView: KeyguardStatusView? = null + init { addIndicationTextArea() addLockIconView() @@ -45,6 +48,7 @@ class KeyguardRootView( addLeftShortcut() addRightShortcut() addSettingsPopupMenu() + addStatusView() } private fun addIndicationTextArea() { @@ -119,4 +123,19 @@ class KeyguardRootView( } addView(view) } + + fun addStatusView(): KeyguardStatusView { + // StatusView may need to be rebuilt on config changes. Remove and reinflate + statusView?.let { removeView(it) } + val view = + (LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, this, false) + as KeyguardStatusView) + .apply { + setClipChildren(false) + statusView = this + } + + addView(view) + return view + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt index 5538fe7e7006..518df0719aaa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt @@ -25,6 +25,8 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAr import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection +import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines import javax.inject.Inject /** @@ -42,6 +44,8 @@ constructor( private val defaultShortcutsSection: DefaultShortcutsSection, private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection, private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection, + private val defaultStatusViewSection: DefaultStatusViewSection, + private val splitShadeGuidelines: SplitShadeGuidelines, ) : KeyguardBlueprint { override val id: String = DEFAULT @@ -51,6 +55,8 @@ constructor( defaultShortcutsSection.apply(constraintSet) defaultAmbientIndicationAreaSection.apply(constraintSet) defaultSettingsPopupMenuSection.apply(constraintSet) + defaultStatusViewSection.apply(constraintSet) + splitShadeGuidelines.apply(constraintSet) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt index 19410e4ba89d..54c27960db3c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt @@ -27,6 +27,8 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAr import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection +import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines import javax.inject.Inject /** Vertically aligns the shortcuts with the udfps. */ @@ -41,6 +43,8 @@ constructor( private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection, private val alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection, private val defaultShortcutsSection: DefaultShortcutsSection, + private val defaultStatusViewSection: DefaultStatusViewSection, + private val splitShadeGuidelines: SplitShadeGuidelines, ) : KeyguardBlueprint { override val id: String = SHORTCUTS_BESIDE_UDFPS @@ -54,6 +58,8 @@ constructor( } else { defaultShortcutsSection.apply(constraintSet) } + defaultStatusViewSection.apply(constraintSet) + splitShadeGuidelines.apply(constraintSet) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt new file mode 100644 index 000000000000..3f319ba2d0e4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.ui.view.layout.sections + +import android.content.Context +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.R +import com.android.systemui.keyguard.data.repository.KeyguardSection +import javax.inject.Inject +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import androidx.constraintlayout.widget.ConstraintSet.END + +class DefaultStatusViewSection @Inject constructor(private val context: Context) : + KeyguardSection { + private val statusViewId = R.id.keyguard_status_view + + override fun apply(constraintSet: ConstraintSet) { + constraintSet.apply { + constrainWidth(statusViewId, MATCH_CONSTRAINT) + constrainHeight(statusViewId, WRAP_CONTENT) + connect(statusViewId, TOP, PARENT_ID, TOP) + connect(statusViewId, START, PARENT_ID, START) + connect(statusViewId, END, PARENT_ID, END) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt new file mode 100644 index 000000000000..668b17ffeba0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.ui.view.layout.sections + +import android.content.Context +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.R +import com.android.systemui.keyguard.data.repository.KeyguardSection +import javax.inject.Inject +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.VERTICAL + +class SplitShadeGuidelines @Inject constructor(private val context: Context) : + KeyguardSection { + + override fun apply(constraintSet: ConstraintSet) { + constraintSet.apply { + // For use on large screens, it will provide a guideline vertically in the center to + // enable items to be aligned on the left or right sides + create(R.id.split_shade_guideline, VERTICAL) + setGuidelinePercent(R.id.split_shade_guideline, 0.5f) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index abd178ca6c1d..f46d0eb449a0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -17,14 +17,17 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.R +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.shared.model.SceneKey import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map @@ -36,36 +39,37 @@ class LockscreenSceneViewModel @Inject constructor( @Application applicationScope: CoroutineScope, - private val interactor: LockscreenSceneInteractor, + authenticationInteractor: AuthenticationInteractor, + private val bouncerInteractor: BouncerInteractor, ) { /** The icon for the "lock" button on the lockscreen. */ val lockButtonIcon: StateFlow<Icon> = - interactor.isDeviceLocked - .map { isLocked -> lockIcon(isLocked = isLocked) } + authenticationInteractor.isUnlocked + .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = lockIcon(isLocked = interactor.isDeviceLocked.value), + initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value), ) /** The key of the scene we should switch to when swiping up. */ - val upDestinationSceneKey: StateFlow<SceneKey> = - interactor.isSwipeToDismissEnabled - .map { isSwipeToUnlockEnabled -> upDestinationSceneKey(isSwipeToUnlockEnabled) } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = upDestinationSceneKey(interactor.isSwipeToDismissEnabled.value), - ) + val upDestinationSceneKey: Flow<SceneKey> = + authenticationInteractor.authenticationMethod.map { authenticationMethod -> + if (authenticationMethod is AuthenticationMethodModel.Swipe) { + SceneKey.Gone + } else { + SceneKey.Bouncer + } + } /** Notifies that the lock button on the lock screen was clicked. */ fun onLockButtonClicked() { - interactor.dismissLockscreen() + bouncerInteractor.showOrUnlockDevice() } /** Notifies that some content on the lock screen was clicked. */ fun onContentClicked() { - interactor.dismissLockscreen() + bouncerInteractor.showOrUnlockDevice() } private fun upDestinationSceneKey( @@ -75,22 +79,22 @@ constructor( } private fun lockIcon( - isLocked: Boolean, + isUnlocked: Boolean, ): Icon { return Icon.Resource( res = - if (isLocked) { - R.drawable.ic_device_lock_on - } else { + if (isUnlocked) { R.drawable.ic_device_lock_off + } else { + R.drawable.ic_device_lock_on }, contentDescription = ContentDescription.Resource( res = - if (isLocked) { - R.string.accessibility_lock_icon - } else { + if (isUnlocked) { R.string.accessibility_unlock_button + } else { + R.string.accessibility_lock_icon } ) ) diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java deleted file mode 100644 index d1d3e3de39f0..000000000000 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.people; - -import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID; -import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; - -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; -import android.view.ViewGroup; - -import androidx.activity.ComponentActivity; -import androidx.lifecycle.ViewModelProvider; - -import com.android.systemui.compose.ComposeFacade; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; -import com.android.systemui.people.ui.view.PeopleViewBinder; -import com.android.systemui.people.ui.viewmodel.PeopleViewModel; - -import javax.inject.Inject; - -import kotlin.Unit; -import kotlin.jvm.functions.Function1; - -/** People Tile Widget configuration activity that shows the user their conversation tiles. */ -public class PeopleSpaceActivity extends ComponentActivity { - - private static final String TAG = "PeopleSpaceActivity"; - private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; - - private final PeopleViewModel.Factory mViewModelFactory; - private final FeatureFlags mFeatureFlags; - - @Inject - public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory, - FeatureFlags featureFlags) { - super(); - mViewModelFactory = viewModelFactory; - mFeatureFlags = featureFlags; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setResult(RESULT_CANCELED); - - PeopleViewModel viewModel = new ViewModelProvider(this, mViewModelFactory).get( - PeopleViewModel.class); - - // Update the widget ID coming from the intent. - int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); - viewModel.onWidgetIdChanged(widgetId); - - Function1<PeopleViewModel.Result, Unit> onResult = (result) -> { - finishActivity(result); - return null; - }; - - if (mFeatureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE) - && ComposeFacade.INSTANCE.isComposeAvailable()) { - Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity"); - ComposeFacade.INSTANCE.setPeopleSpaceActivityContent(this, viewModel, onResult); - } else { - Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity"); - ViewGroup view = PeopleViewBinder.create(this); - PeopleViewBinder.bind(view, viewModel, /* lifecycleOwner= */ this, onResult); - setContentView(view); - } - } - - private void finishActivity(PeopleViewModel.Result result) { - if (result instanceof PeopleViewModel.Result.Success) { - if (DEBUG) Log.d(TAG, "Widget added!"); - Intent data = ((PeopleViewModel.Result.Success) result).getData(); - setResult(RESULT_OK, data); - } else { - if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!"); - setResult(RESULT_CANCELED); - } - finish(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt new file mode 100644 index 000000000000..5b7eb454597c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt @@ -0,0 +1,99 @@ +/* + * 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.people + +import android.appwidget.AppWidgetManager +import android.os.Bundle +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.compose.ComposeFacade.isComposeAvailable +import com.android.systemui.compose.ComposeFacade.setPeopleSpaceActivityContent +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.people.ui.view.PeopleViewBinder +import com.android.systemui.people.ui.view.PeopleViewBinder.bind +import com.android.systemui.people.ui.viewmodel.PeopleViewModel +import javax.inject.Inject +import kotlinx.coroutines.launch + +/** People Tile Widget configuration activity that shows the user their conversation tiles. */ +class PeopleSpaceActivity +@Inject +constructor( + private val viewModelFactory: PeopleViewModel.Factory, + private val featureFlags: FeatureFlags, +) : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_CANCELED) + + // Update the widget ID coming from the intent. + val viewModel = ViewModelProvider(this, viewModelFactory)[PeopleViewModel::class.java] + val widgetId = + intent.getIntExtra( + AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID, + ) + viewModel.onWidgetIdChanged(widgetId) + + // Make sure to refresh the tiles/conversations when the lifecycle is resumed, so that it + // updates them when going back to the Activity after leaving it. + // Note that we do this here instead of inside an effect in the PeopleScreen() composable + // because otherwise onTileRefreshRequested() will be called after the first composition, + // which will trigger a new recomposition and redraw, affecting the GPU memory (see + // b/276871425). + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { viewModel.onTileRefreshRequested() } + } + + // Set the content of the activity, using either the View or Compose implementation. + if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE) && isComposeAvailable()) { + Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity") + setPeopleSpaceActivityContent( + activity = this, + viewModel, + onResult = { finishActivity(it) }, + ) + } else { + Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity") + val view = PeopleViewBinder.create(this) + bind(view, viewModel, lifecycleOwner = this, onResult = { finishActivity(it) }) + setContentView(view) + } + } + + private fun finishActivity(result: PeopleViewModel.Result) { + if (result is PeopleViewModel.Result.Success) { + if (DEBUG) Log.d(TAG, "Widget added!") + setResult(RESULT_OK, result.data) + } else { + if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!") + setResult(RESULT_CANCELED) + } + + finish() + } + + companion object { + private const val TAG = "PeopleSpaceActivity" + private const val DEBUG = PeopleSpaceUtils.DEBUG + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt index d8a429e5bb1a..5f338c30c966 100644 --- a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt @@ -109,14 +109,6 @@ object PeopleViewBinder { } } } - - // Make sure to refresh the tiles/conversations when the Activity is resumed, so that it - // updates them when going back to the Activity after leaving it. - lifecycleOwner.lifecycleScope.launch { - lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { - viewModel.onTileRefreshRequested() - } - } } private fun setNoConversationsContent(view: ViewGroup, onGotItClicked: () -> Unit) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt index 5e6a44bf8130..4c6281e1cdb0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt @@ -16,19 +16,17 @@ package com.android.systemui.qs.ui.viewmodel +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import javax.inject.Inject /** Models UI state and handles user input for the quick settings scene. */ @SysUISingleton class QuickSettingsSceneViewModel @Inject -constructor( - private val lockscreenSceneInteractor: LockscreenSceneInteractor, -) { +constructor(private val bouncerInteractor: BouncerInteractor) { /** Notifies that some content in quick settings was clicked. */ fun onContentClicked() { - lockscreenSceneInteractor.dismissLockscreen() + bouncerInteractor.showOrUnlockDevice() } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 20ee393e8dac..bd233f80b47b 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.domain.startable import com.android.systemui.CoreStartable import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.DisplayId @@ -135,22 +136,27 @@ constructor( applicationScope.launch { keyguardInteractor.wakefulnessModel - .map { it.state == WakefulnessState.ASLEEP } + .map { wakefulnessModel -> wakefulnessModel.state } .distinctUntilChanged() - .collect { isAsleep -> - if (isAsleep) { - // When the device goes to sleep, reset the current scene. - val isUnlocked = authenticationInteractor.isUnlocked.value - val (targetSceneKey, loggingReason) = - if (isUnlocked) { - SceneKey.Gone to "device is asleep while unlocked" - } else { - SceneKey.Lockscreen to "device is asleep while locked" + .collect { wakefulnessState -> + when (wakefulnessState) { + WakefulnessState.STARTING_TO_SLEEP -> { + switchToScene( + targetSceneKey = SceneKey.Lockscreen, + loggingReason = "device is asleep", + ) + } + WakefulnessState.STARTING_TO_WAKE -> { + val authMethod = authenticationInteractor.getAuthenticationMethod() + if (authMethod == AuthenticationMethodModel.None) { + switchToScene( + targetSceneKey = SceneKey.Gone, + loggingReason = + "device is starting to wake up while auth method is None", + ) } - switchToScene( - targetSceneKey = targetSceneKey, - loggingReason = loggingReason, - ) + } + else -> Unit } } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 416f147b7429..35fd98cf34c6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -90,6 +90,8 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.widget.FrameLayout; +import androidx.constraintlayout.widget.ConstraintLayout; + import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -1053,10 +1055,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusBarViewController.init(); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); - updateViewControllers( - mView.findViewById(R.id.keyguard_status_view), - userAvatarContainer, - keyguardUserSwitcherView); + updateViewControllers(userAvatarContainer, keyguardUserSwitcherView); mNotificationStackScrollLayoutController.setOnHeightChangedListener( new NsslHeightChangedListener()); @@ -1218,18 +1217,31 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mQsController.loadDimens(); } - private void updateViewControllers(KeyguardStatusView keyguardStatusView, + private void updateViewControllers( FrameLayout userAvatarView, KeyguardUserSwitcherView keyguardUserSwitcherView) { + // Re-associate the KeyguardStatusViewController if (mKeyguardStatusViewController != null) { mKeyguardStatusViewController.onDestroy(); } - // Re-associate the KeyguardStatusViewController - KeyguardStatusViewComponent statusViewComponent = + + if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { + // Need a shared controller until mKeyguardStatusViewController can be removed from + // here, due to important state being set in that controller. Rebind in order to pick + // up config changes + mKeyguardViewConfigurator.bindKeyguardStatusView(mView); + mKeyguardStatusViewController = + mKeyguardViewConfigurator.getKeyguardStatusViewController(); + } else { + KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById( + R.id.keyguard_status_view); + KeyguardStatusViewComponent statusViewComponent = mKeyguardStatusViewComponentFactory.build(keyguardStatusView); - mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); - mKeyguardStatusViewController.init(); + mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); + mKeyguardStatusViewController.init(); + } mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled); + updateClockAppearance(); if (mKeyguardUserSwitcherController != null) { @@ -1335,15 +1347,22 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump void reInflateViews() { debugLog("reInflateViews"); // Re-inflate the status view group. - KeyguardStatusView keyguardStatusView = - mNotificationContainerParent.findViewById(R.id.keyguard_status_view); - int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView); - mNotificationContainerParent.removeView(keyguardStatusView); - keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate( - R.layout.keyguard_status_view, mNotificationContainerParent, false); - mNotificationContainerParent.addView(keyguardStatusView, statusIndex); - attachSplitShadeMediaPlayerContainer( - keyguardStatusView.findViewById(R.id.status_view_media_container)); + if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { + KeyguardStatusView keyguardStatusView = + mNotificationContainerParent.findViewById(R.id.keyguard_status_view); + int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView); + mNotificationContainerParent.removeView(keyguardStatusView); + keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate( + R.layout.keyguard_status_view, mNotificationContainerParent, false); + mNotificationContainerParent.addView(keyguardStatusView, statusIndex); + + attachSplitShadeMediaPlayerContainer( + keyguardStatusView.findViewById(R.id.status_view_media_container)); + } else { + attachSplitShadeMediaPlayerContainer( + mKeyguardViewConfigurator.getKeyguardRootView() + .findViewById(R.id.status_view_media_container)); + } // we need to update KeyguardStatusView constraints after reinflating it updateResources(); @@ -1369,8 +1388,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump R.layout.keyguard_user_switcher /* layoutId */, showKeyguardUserSwitcher /* enabled */); - updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView, - keyguardUserSwitcherView); + updateViewControllers(userAvatarView, keyguardUserSwitcherView); if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { // Update keyguard bottom area @@ -1666,8 +1684,14 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void updateKeyguardStatusViewAlignment(boolean animate) { boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered(); + ConstraintLayout layout; + if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { + layout = mKeyguardViewConfigurator.getKeyguardRootView(); + } else { + layout = mNotificationContainerParent; + } mKeyguardStatusViewController.updateAlignment( - mNotificationContainerParent, mSplitShadeEnabled, shouldBeCentered, animate); + layout, mSplitShadeEnabled, shouldBeCentered, animate); mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); } @@ -3390,7 +3414,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ipw.print("mPanelFlingOvershootAmount="); ipw.println(mPanelFlingOvershootAmount); ipw.print("mLastGesturedOverExpansion="); ipw.println(mLastGesturedOverExpansion); ipw.print("mIsSpringBackAnimation="); ipw.println(mIsSpringBackAnimation); - ipw.print("mSplitShadeEnabled="); ipw.println(mSplitShadeEnabled); ipw.print("mHintDistance="); ipw.println(mHintDistance); ipw.print("mInitialOffsetOnTouch="); ipw.println(mInitialOffsetOnTouch); ipw.print("mCollapsedAndHeadsUpOnDown="); ipw.println(mCollapsedAndHeadsUpOnDown); @@ -3423,7 +3446,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ipw.print("mGestureWaitForTouchSlop="); ipw.println(mGestureWaitForTouchSlop); ipw.print("mIgnoreXTouchSlop="); ipw.println(mIgnoreXTouchSlop); ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking); - ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking); ipw.println("gestureExclusionRect:" + calculateGestureExclusionRect()); new DumpsysTableLogger( TAG, diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt index 0b3ed5601c2e..87abc9208d45 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt @@ -16,9 +16,10 @@ package com.android.systemui.shade.ui.viewmodel +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.shared.model.SceneKey import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -33,29 +34,30 @@ class ShadeSceneViewModel @Inject constructor( @Application private val applicationScope: CoroutineScope, - private val lockscreenSceneInteractor: LockscreenSceneInteractor, + authenticationInteractor: AuthenticationInteractor, + private val bouncerInteractor: BouncerInteractor, ) { /** The key of the scene we should switch to when swiping up. */ val upDestinationSceneKey: StateFlow<SceneKey> = - lockscreenSceneInteractor.isDeviceLocked - .map { isLocked -> upDestinationSceneKey(isLocked = isLocked) } + authenticationInteractor.isUnlocked + .map { isUnlocked -> upDestinationSceneKey(isUnlocked = isUnlocked) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = upDestinationSceneKey( - isLocked = lockscreenSceneInteractor.isDeviceLocked.value, + isUnlocked = authenticationInteractor.isUnlocked.value, ), ) /** Notifies that some content in the shade was clicked. */ fun onContentClicked() { - lockscreenSceneInteractor.dismissLockscreen() + bouncerInteractor.showOrUnlockDevice() } private fun upDestinationSceneKey( - isLocked: Boolean, + isUnlocked: Boolean, ): SceneKey { - return if (isLocked) SceneKey.Lockscreen else SceneKey.Gone + return if (isUnlocked) SceneKey.Gone else SceneKey.Lockscreen } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index 94251ffc74c8..efd7d2ef2718 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -446,6 +446,12 @@ constructor( session?.requestSmartspaceUpdate() } + fun removeViewsFromParent(viewGroup: ViewGroup) { + smartspaceViews.toList().forEach { + viewGroup.removeView(it as View) + } + } + /** * Disconnects the smartspace view from the smartspace service and cleans up any resources. */ 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 5e7e4be60104..1b790fdc35c1 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 @@ -321,7 +321,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView protected void setBackgroundTintColor(int color) { if (color != mCurrentBackgroundTint) { mCurrentBackgroundTint = color; - if (color == mNormalColor) { + // TODO(282173943): re-enable this tinting optimization when Resources are thread-safe + if (false && color == mNormalColor) { // We don't need to tint a normal notification color = 0; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 6bbeebfdb431..0989df61a5e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -16,11 +16,15 @@ package com.android.systemui.statusbar.notification.row; +import static android.graphics.PorterDuff.Mode.SRC_ATOP; + import android.annotation.ColorInt; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.ColorFilter; +import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -157,10 +161,20 @@ public class FooterView extends StackScrollerDecorView { */ public void updateColors() { Resources.Theme theme = mContext.getTheme(); - int textColor = getResources().getColor(R.color.notif_pill_text, theme); - mClearAllButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + final @ColorInt int textColor = getResources().getColor(R.color.notif_pill_text, theme); + final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background); + final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background); + // TODO(b/282173943): Remove redundant tinting once Resources are thread-safe + final @ColorInt int buttonBgColor = + Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface); + final ColorFilter bgColorFilter = new PorterDuffColorFilter(buttonBgColor, SRC_ATOP); + if (buttonBgColor != 0) { + clearAllBg.setColorFilter(bgColorFilter); + manageBg.setColorFilter(bgColorFilter); + } + mClearAllButton.setBackground(clearAllBg); mClearAllButton.setTextColor(textColor); - mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + mManageButton.setBackground(manageBg); mManageButton.setTextColor(textColor); final @ColorInt int labelTextColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index dcd18dd7d1bf..2ccbc9f2f017 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -401,7 +401,7 @@ constructor( isActivityIntent: Boolean, showOverLockscreen: Boolean, ): Boolean { - // TODO(b/184121838): Support launch animations when occluded. + // TODO(b/294418322): Support launch animations when occluded. if (keyguardStateController.isOccluded) { return false } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt index 3a11635f75c3..c1af6df12bd1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt @@ -118,6 +118,11 @@ interface MobileConnectionRepository { /** The service provider name for this network connection, or the default name */ val networkName: StateFlow<NetworkNameModel> + /** + * True if this type of connection is allowed while airplane mode is on, and false otherwise. + */ + val isAllowedDuringAirplaneMode: StateFlow<Boolean> + companion object { /** The default number of levels to use for [numberOfLevels]. */ const val DEFAULT_NUM_LEVELS = 4 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt index 6b86432b8171..17d20c297861 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt @@ -186,6 +186,8 @@ class DemoMobileConnectionRepository( override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo network")) + override val isAllowedDuringAirplaneMode = MutableStateFlow(false) + /** * Process a new demo mobile event. Note that [resolvedNetworkType] must be passed in separately * from the event, due to the requirement to reverse the mobile mappings lookup in the top-level @@ -217,6 +219,8 @@ class DemoMobileConnectionRepository( (event.activity ?: TelephonyManager.DATA_ACTIVITY_NONE).toMobileDataActivityModel() _carrierNetworkChangeActive.value = event.carrierNetworkChange _resolvedNetworkType.value = resolvedNetworkType + + isAllowedDuringAirplaneMode.value = false } fun processCarrierMergedEvent(event: FakeWifiEventModel.CarrierMerged) { @@ -240,6 +244,7 @@ class DemoMobileConnectionRepository( _isInService.value = true _isGsm.value = false _carrierNetworkChangeActive.value = false + isAllowedDuringAirplaneMode.value = true } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt index a609917351d9..65f486683837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt @@ -165,6 +165,13 @@ class CarrierMergedConnectionRepository( override val isGsm = MutableStateFlow(false).asStateFlow() override val carrierNetworkChangeActive = MutableStateFlow(false).asStateFlow() + /** + * Carrier merged connections happen over wifi but are displayed as a mobile triangle. Because + * they occur over wifi, it's possible to have a valid carrier merged connection even during + * airplane mode. See b/291993542. + */ + override val isAllowedDuringAirplaneMode = MutableStateFlow(true).asStateFlow() + override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt index 8869dfe02697..8ba7d2197c14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt @@ -287,6 +287,15 @@ class FullMobileConnectionRepository( ) .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value) + override val isAllowedDuringAirplaneMode = + activeRepo + .flatMapLatest { it.isAllowedDuringAirplaneMode } + .stateIn( + scope, + SharingStarted.WhileSubscribed(), + activeRepo.value.isAllowedDuringAirplaneMode.value, + ) + class Factory @Inject constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt index b475183d98c6..aadc975a10de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt @@ -59,8 +59,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.asExecutor 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.asStateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -331,6 +333,9 @@ class MobileConnectionRepositoryImpl( .stateIn(scope, SharingStarted.WhileSubscribed(), initial) } + /** Typical mobile connections aren't available during airplane mode. */ + override val isAllowedDuringAirplaneMode = MutableStateFlow(false).asStateFlow() + class Factory @Inject constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index d42e30c9b1b9..1a138272d67c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -111,6 +111,9 @@ interface MobileIconInteractor { /** See [MobileIconsInteractor.isForceHidden]. */ val isForceHidden: Flow<Boolean> + /** See [MobileConnectionRepository.isAllowedDuringAirplaneMode]. */ + val isAllowedDuringAirplaneMode: StateFlow<Boolean> + /** True when in carrier network change mode */ val carrierNetworkChangeActive: StateFlow<Boolean> } @@ -267,4 +270,6 @@ class MobileIconInteractorImpl( .stateIn(scope, SharingStarted.WhileSubscribed(), false) override val isInService = connectionRepository.isInService + + override val isAllowedDuringAirplaneMode = connectionRepository.isAllowedDuringAirplaneMode } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt index 35f4f9aa4622..fe2481595ff4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt @@ -102,9 +102,16 @@ constructor( } else { combine( airplaneModeInteractor.isAirplaneMode, + iconInteractor.isAllowedDuringAirplaneMode, iconInteractor.isForceHidden, - ) { isAirplaneMode, isForceHidden -> - !isAirplaneMode && !isForceHidden + ) { isAirplaneMode, isAllowedDuringAirplaneMode, isForceHidden -> + if (isForceHidden) { + false + } else if (isAirplaneMode) { + isAllowedDuringAirplaneMode + } else { + true + } } } .distinctUntilChanged() diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java index ac04bc4356ac..98d4d22d59b4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -23,6 +23,7 @@ import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLE import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -157,6 +158,12 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView); when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView); when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); + doAnswer(invocation -> { + removeView(mFakeDateView); + removeView(mFakeWeatherView); + removeView(mFakeSmartspaceView); + return null; + }).when(mSmartspaceController).removeViewsFromParent(any()); mExecutor = new FakeExecutor(new FakeSystemClock()); mFakeFeatureFlags = new FakeFeatureFlags(); mFakeFeatureFlags.set(FACE_AUTH_REFACTOR, false); @@ -201,6 +208,13 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea); } + private void removeView(View v) { + ViewGroup group = ((ViewGroup) v.getParent()); + if (group != null) { + group.removeView(v); + } + } + protected void init() { mController.init(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index 20d9ef1e86b1..7d23c800321a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -119,8 +120,8 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll @Test public void correctlyDump() { mController.onInit(); - verify(mDumpManager).registerDumpable(mController); + verify(mDumpManager).registerDumpable(eq(mController.getInstanceName()), eq(mController)); mController.onDestroy(); - verify(mDumpManager, times(1)).unregisterDumpable(KeyguardStatusViewController.TAG); + verify(mDumpManager, times(1)).unregisterDumpable(eq(mController.getInstanceName())); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt index 005697044c0f..d3a2a73959dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt @@ -18,23 +18,30 @@ package com.android.systemui.authentication.data.repository +import android.app.admin.DevicePolicyManager +import android.content.Intent import android.content.pm.UserInfo import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityModel import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.scene.SceneTestUtils import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import java.util.function.Function import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.MockitoAnnotations @@ -43,6 +50,7 @@ import org.mockito.MockitoAnnotations class AuthenticationRepositoryTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils + @Mock private lateinit var getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode> private val testUtils = SceneTestUtils(this) private val testScope = testUtils.testScope @@ -50,24 +58,49 @@ class AuthenticationRepositoryTest : SysuiTestCase() { private lateinit var underTest: AuthenticationRepository + private var currentSecurityMode: KeyguardSecurityModel.SecurityMode = + KeyguardSecurityModel.SecurityMode.PIN + @Before fun setUp() { MockitoAnnotations.initMocks(this) userRepository.setUserInfos(USER_INFOS) runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) } + whenever(getSecurityMode.apply(anyInt())).thenAnswer { currentSecurityMode } underTest = AuthenticationRepositoryImpl( applicationScope = testScope.backgroundScope, - getSecurityMode = { KeyguardSecurityModel.SecurityMode.PIN }, + getSecurityMode = getSecurityMode, backgroundDispatcher = testUtils.testDispatcher, userRepository = userRepository, keyguardRepository = testUtils.keyguardRepository, lockPatternUtils = lockPatternUtils, + broadcastDispatcher = fakeBroadcastDispatcher, ) } @Test + fun authenticationMethod() = + testScope.runTest { + val authMethod by collectLastValue(underTest.authenticationMethod) + runCurrent() + dispatchBroadcast() + assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin) + assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Pin) + + setSecurityModeAndDispatchBroadcast(KeyguardSecurityModel.SecurityMode.Pattern) + assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pattern) + assertThat(underTest.getAuthenticationMethod()) + .isEqualTo(AuthenticationMethodModel.Pattern) + + setSecurityModeAndDispatchBroadcast(KeyguardSecurityModel.SecurityMode.None) + assertThat(authMethod).isEqualTo(AuthenticationMethodModel.None) + assertThat(underTest.getAuthenticationMethod()) + .isEqualTo(AuthenticationMethodModel.None) + } + + @Test fun isAutoConfirmEnabled() = testScope.runTest { whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[0].id)).thenReturn(true) @@ -95,6 +128,20 @@ class AuthenticationRepositoryTest : SysuiTestCase() { assertThat(values.last()).isTrue() } + private fun setSecurityModeAndDispatchBroadcast( + securityMode: KeyguardSecurityModel.SecurityMode, + ) { + currentSecurityMode = securityMode + dispatchBroadcast() + } + + private fun dispatchBroadcast() { + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED) + ) + } + companion object { private val USER_INFOS = listOf( diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index a86937fcad3c..d848cd46e509 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -19,9 +19,11 @@ package com.android.systemui.authentication.domain.interactor import android.app.admin.DevicePolicyManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel import com.android.systemui.authentication.data.repository.AuthenticationRepository import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils @@ -50,42 +52,61 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) @Test - fun getAuthenticationMethod() = + fun authenticationMethod() = testScope.runTest { - assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Pin) + val authMethod by collectLastValue(underTest.authenticationMethod) + runCurrent() + assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Pin) + assertThat(underTest.getAuthenticationMethod()) + .isEqualTo(DomainLayerAuthenticationMethodModel.Pin) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password + DataLayerAuthenticationMethodModel.Password ) + assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Password) assertThat(underTest.getAuthenticationMethod()) - .isEqualTo(AuthenticationMethodModel.Password) + .isEqualTo(DomainLayerAuthenticationMethodModel.Password) } @Test - fun getAuthenticationMethod_noneTreatedAsSwipe_whenLockscreenEnabled() = + fun authenticationMethod_noneTreatedAsSwipe_whenLockscreenEnabled() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + val authMethod by collectLastValue(underTest.authenticationMethod) + runCurrent() + + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) utils.authenticationRepository.setLockscreenEnabled(true) + assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Swipe) assertThat(underTest.getAuthenticationMethod()) - .isEqualTo(AuthenticationMethodModel.Swipe) + .isEqualTo(DomainLayerAuthenticationMethodModel.Swipe) } @Test - fun getAuthenticationMethod_none_whenLockscreenDisabled() = + fun authenticationMethod_none_whenLockscreenDisabled() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + val authMethod by collectLastValue(underTest.authenticationMethod) + runCurrent() + + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) utils.authenticationRepository.setLockscreenEnabled(false) + assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.None) assertThat(underTest.getAuthenticationMethod()) - .isEqualTo(AuthenticationMethodModel.None) + .isEqualTo(DomainLayerAuthenticationMethodModel.None) } @Test fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) utils.authenticationRepository.setLockscreenEnabled(false) val isUnlocked by collectLastValue(underTest.isUnlocked) @@ -111,7 +132,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { @Test fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isFalse() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) utils.authenticationRepository.setLockscreenEnabled(true) val isUnlocked by collectLastValue(underTest.isUnlocked) @@ -124,7 +147,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.setUnlocked(false) runCurrent() utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password + DataLayerAuthenticationMethodModel.Password ) assertThat(underTest.isAuthenticationRequired()).isTrue() @@ -135,7 +158,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { testScope.runTest { utils.authenticationRepository.setUnlocked(false) runCurrent() - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) assertThat(underTest.isAuthenticationRequired()).isFalse() } @@ -146,7 +171,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.setUnlocked(true) runCurrent() utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password + DataLayerAuthenticationMethodModel.Password ) assertThat(underTest.isAuthenticationRequired()).isFalse() @@ -157,7 +182,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { testScope.runTest { utils.authenticationRepository.setUnlocked(true) runCurrent() - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) assertThat(underTest.isAuthenticationRequired()).isFalse() } @@ -166,7 +193,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun authenticate_withCorrectPin_returnsTrue() = testScope.runTest { val isThrottled by collectLastValue(underTest.isThrottled) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() assertThat(isThrottled).isFalse() } @@ -174,21 +203,27 @@ class AuthenticationInteractorTest : SysuiTestCase() { @Test fun authenticate_withIncorrectPin_returnsFalse() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4))).isFalse() } @Test(expected = IllegalArgumentException::class) fun authenticate_withEmptyPin_throwsException() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) underTest.authenticate(listOf()) } @Test fun authenticate_withCorrectMaxLengthPin_returnsTrue() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) val pin = List(16) { 9 } utils.authenticationRepository.overrideCredential(pin) assertThat(underTest.authenticate(pin)).isTrue() @@ -203,7 +238,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { // If the policy changes, there is work to do in SysUI. assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) assertThat(underTest.authenticate(List(17) { 9 })).isFalse() } @@ -212,7 +249,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { testScope.runTest { val isThrottled by collectLastValue(underTest.isThrottled) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password + DataLayerAuthenticationMethodModel.Password ) assertThat(underTest.authenticate("password".toList())).isTrue() @@ -223,7 +260,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun authenticate_withIncorrectPassword_returnsFalse() = testScope.runTest { utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password + DataLayerAuthenticationMethodModel.Password ) assertThat(underTest.authenticate("alohomora".toList())).isFalse() @@ -233,7 +270,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun authenticate_withCorrectPattern_returnsTrue() = testScope.runTest { utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern + DataLayerAuthenticationMethodModel.Pattern ) assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue() @@ -243,21 +280,21 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun authenticate_withIncorrectPattern_returnsFalse() = testScope.runTest { utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern + DataLayerAuthenticationMethodModel.Pattern ) assertThat( underTest.authenticate( listOf( - AuthenticationMethodModel.Pattern.PatternCoordinate( + AuthenticationPatternCoordinate( x = 2, y = 0, ), - AuthenticationMethodModel.Pattern.PatternCoordinate( + AuthenticationPatternCoordinate( x = 2, y = 1, ), - AuthenticationMethodModel.Pattern.PatternCoordinate( + AuthenticationPatternCoordinate( x = 2, y = 2, ), @@ -271,7 +308,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() = testScope.runTest { val isThrottled by collectLastValue(underTest.isThrottled) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.setAutoConfirmEnabled(true) assertThat( underTest.authenticate( @@ -289,7 +328,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = testScope.runTest { val isUnlocked by collectLastValue(underTest.isUnlocked) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.setAutoConfirmEnabled(true) assertThat( underTest.authenticate( @@ -305,7 +346,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = testScope.runTest { val isUnlocked by collectLastValue(underTest.isUnlocked) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.setAutoConfirmEnabled(true) assertThat( underTest.authenticate( @@ -321,7 +364,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = testScope.runTest { val isUnlocked by collectLastValue(underTest.isUnlocked) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.setAutoConfirmEnabled(true) assertThat( underTest.authenticate( @@ -337,7 +382,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = testScope.runTest { val isUnlocked by collectLastValue(underTest.isUnlocked) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.setAutoConfirmEnabled(false) assertThat( underTest.authenticate( @@ -354,7 +401,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { testScope.runTest { val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password + DataLayerAuthenticationMethodModel.Password ) assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull() @@ -367,7 +414,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { val isUnlocked by collectLastValue(underTest.isUnlocked) val throttling by collectLastValue(underTest.throttling) val isThrottled by collectLastValue(underTest.isThrottled) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) assertThat(isUnlocked).isTrue() assertThat(isThrottled).isFalse() @@ -456,7 +505,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun hintedPinLength_withoutAutoConfirm_isNull() = testScope.runTest { val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.setAutoConfirmEnabled(false) assertThat(hintedPinLength).isNull() @@ -466,7 +517,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun hintedPinLength_withAutoConfirmPinTooShort_isNull() = testScope.runTest { val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.overrideCredential( buildList { repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) } @@ -481,7 +534,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun hintedPinLength_withAutoConfirmPinAtRightLength_isSameLength() = testScope.runTest { val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.setAutoConfirmEnabled(true) utils.authenticationRepository.overrideCredential( buildList { repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) } } @@ -494,7 +549,9 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun hintedPinLength_withAutoConfirmPinTooLong_isNull() = testScope.runTest { val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) utils.authenticationRepository.overrideCredential( buildList { repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index 186df02536ea..38e5728f6e70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -165,6 +165,28 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { assertFalse(bouncerRepository.alternateBouncerVisible.value) } + @Test + fun alternateBouncerUiAvailable_fromMultipleSources() { + assertFalse(bouncerRepository.alternateBouncerUIAvailable.value) + + // GIVEN there are two different sources indicating the alternate bouncer is available + underTest.setAlternateBouncerUIAvailable(true, "source1") + underTest.setAlternateBouncerUIAvailable(true, "source2") + assertTrue(bouncerRepository.alternateBouncerUIAvailable.value) + + // WHEN one of the sources no longer says the UI is available + underTest.setAlternateBouncerUIAvailable(false, "source1") + + // THEN alternate bouncer UI is still available (from the other source) + assertTrue(bouncerRepository.alternateBouncerUIAvailable.value) + + // WHEN all sources say the UI is not available + underTest.setAlternateBouncerUIAvailable(false, "source2") + + // THEN alternate boucer UI is not available + assertFalse(bouncerRepository.alternateBouncerUIAvailable.value) + } + private fun givenCanShowAlternateBouncer() { bouncerRepository.setAlternateBouncerUIAvailable(true) biometricSettingsRepository.setFingerprintEnrolled(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 14fc931522a4..df4d2225f459 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -19,8 +19,9 @@ package com.android.systemui.bouncer.domain.interactor import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils @@ -73,6 +74,7 @@ class BouncerInteractorTest : SysuiTestCase() { val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + runCurrent() utils.authenticationRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -104,6 +106,7 @@ class BouncerInteractorTest : SysuiTestCase() { val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + runCurrent() utils.authenticationRepository.setAutoConfirmEnabled(true) utils.authenticationRepository.setUnlocked(false) underTest.showOrUnlockDevice() @@ -140,6 +143,7 @@ class BouncerInteractorTest : SysuiTestCase() { val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + runCurrent() utils.authenticationRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -170,6 +174,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) + runCurrent() utils.authenticationRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -202,6 +207,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pattern ) + runCurrent() utils.authenticationRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -214,11 +220,7 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) // Wrong input. - assertThat( - underTest.authenticate( - listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(1, 2)) - ) - ) + assertThat(underTest.authenticate(listOf(AuthenticationPatternCoordinate(1, 2)))) .isFalse() assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -248,7 +250,8 @@ class BouncerInteractorTest : SysuiTestCase() { fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setLockscreenEnabled(true) utils.authenticationRepository.setUnlocked(false) underTest.showOrUnlockDevice() @@ -264,6 +267,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) + runCurrent() utils.authenticationRepository.setUnlocked(false) val customMessage = "Hello there!" diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index 2cc949326fa0..7af8a0425402 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -18,19 +18,17 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class AuthMethodBouncerViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 0df0a17931f4..2c96bcc9dd33 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -18,8 +18,9 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat @@ -55,6 +56,7 @@ class BouncerViewModelTest : SysuiTestCase() { private val underTest = utils.bouncerViewModel( bouncerInteractor = bouncerInteractor, + authenticationInteractor = authenticationInteractor, ) @Test @@ -86,7 +88,8 @@ class BouncerViewModelTest : SysuiTestCase() { @Test fun authMethod_reusesInstances() = testScope.runTest { - val seen = mutableMapOf<AuthenticationMethodModel, AuthMethodBouncerViewModel>() + val seen = + mutableMapOf<DomainLayerAuthenticationMethodModel, AuthMethodBouncerViewModel>() val authMethodViewModel: AuthMethodBouncerViewModel? by collectLastValue(underTest.authMethod) // First pass, populate our "seen" map: @@ -105,7 +108,7 @@ class BouncerViewModelTest : SysuiTestCase() { @Test fun authMethodsToTest_returnsCompleteSampleOfAllAuthMethodTypes() { assertThat(authMethodsToTest().map { it::class }.toSet()) - .isEqualTo(AuthenticationMethodModel::class.sealedSubclasses.toSet()) + .isEqualTo(DomainLayerAuthenticationMethodModel::class.sealedSubclasses.toSet()) } @Test @@ -113,7 +116,9 @@ class BouncerViewModelTest : SysuiTestCase() { testScope.runTest { val message by collectLastValue(underTest.message) val throttling by collectLastValue(bouncerInteractor.throttling) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) assertThat(message?.isUpdateAnimated).isTrue() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { @@ -136,7 +141,9 @@ class BouncerViewModelTest : SysuiTestCase() { } ) val throttling by collectLastValue(bouncerInteractor.throttling) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) assertThat(isInputEnabled).isTrue() repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { @@ -153,7 +160,9 @@ class BouncerViewModelTest : SysuiTestCase() { fun throttlingDialogMessage() = testScope.runTest { val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.Pin + ) repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { // Wrong PIN. @@ -166,13 +175,32 @@ class BouncerViewModelTest : SysuiTestCase() { assertThat(throttlingDialogMessage).isNull() } - private fun authMethodsToTest(): List<AuthenticationMethodModel> { + private fun authMethodsToTest(): List<DomainLayerAuthenticationMethodModel> { return listOf( - AuthenticationMethodModel.None, - AuthenticationMethodModel.Swipe, - AuthenticationMethodModel.Pin, - AuthenticationMethodModel.Password, - AuthenticationMethodModel.Pattern, + DomainLayerAuthenticationMethodModel.None, + DomainLayerAuthenticationMethodModel.Swipe, + DomainLayerAuthenticationMethodModel.Pin, + DomainLayerAuthenticationMethodModel.Password, + DomainLayerAuthenticationMethodModel.Pattern, + ) + } + + private fun FakeAuthenticationRepository.setAuthenticationMethod( + model: DomainLayerAuthenticationMethodModel, + ) { + setAuthenticationMethod( + when (model) { + is DomainLayerAuthenticationMethodModel.None, + is DomainLayerAuthenticationMethodModel.Swipe -> + DataLayerAuthenticationMethodModel.None + is DomainLayerAuthenticationMethodModel.Pin -> + DataLayerAuthenticationMethodModel.Pin + is DomainLayerAuthenticationMethodModel.Password -> + DataLayerAuthenticationMethodModel.Password + is DomainLayerAuthenticationMethodModel.Pattern -> + DataLayerAuthenticationMethodModel.Pattern + } ) + setLockscreenEnabled(model !is DomainLayerAuthenticationMethodModel.None) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 7f8d54c43387..4e9fe8d91da1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -55,6 +55,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private val bouncerViewModel = utils.bouncerViewModel( bouncerInteractor = bouncerInteractor, + authenticationInteractor = authenticationInteractor, ) private val underTest = PasswordBouncerViewModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 57fcbe595fa6..000200c606b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -19,8 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -57,6 +57,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private val bouncerViewModel = utils.bouncerViewModel( bouncerInteractor = bouncerInteractor, + authenticationInteractor = authenticationInteractor, ) private val underTest = PatternBouncerViewModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 81c68ed2320f..4b667c393b62 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -19,8 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -57,6 +57,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { private val bouncerViewModel = utils.bouncerViewModel( bouncerInteractor = bouncerInteractor, + authenticationInteractor = authenticationInteractor, ) private val underTest = PinBouncerViewModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt deleted file mode 100644 index 86e56bf1e131..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.keyguard.domain.interactor - -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.SceneModel -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(JUnit4::class) -class LockscreenSceneInteractorTest : SysuiTestCase() { - - private val utils = SceneTestUtils(this) - private val testScope = utils.testScope - private val sceneInteractor = utils.sceneInteractor() - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), - ) - private val underTest = - utils.lockScreenSceneInteractor( - authenticationInteractor = authenticationInteractor, - bouncerInteractor = - utils.bouncerInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ), - ) - - @Test - fun isDeviceLocked() = - testScope.runTest { - val isDeviceLocked by collectLastValue(underTest.isDeviceLocked) - - utils.authenticationRepository.setUnlocked(false) - assertThat(isDeviceLocked).isTrue() - - utils.authenticationRepository.setUnlocked(true) - assertThat(isDeviceLocked).isFalse() - } - - @Test - fun isSwipeToDismissEnabled_deviceLockedAndAuthMethodSwipe_true() = - testScope.runTest { - val isSwipeToDismissEnabled by collectLastValue(underTest.isSwipeToDismissEnabled) - - utils.authenticationRepository.setUnlocked(false) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) - - assertThat(isSwipeToDismissEnabled).isTrue() - } - - @Test - fun isSwipeToDismissEnabled_deviceUnlockedAndAuthMethodSwipe_false() = - testScope.runTest { - val isSwipeToDismissEnabled by collectLastValue(underTest.isSwipeToDismissEnabled) - - utils.authenticationRepository.setUnlocked(true) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) - - assertThat(isSwipeToDismissEnabled).isFalse() - } - - @Test - fun dismissLockScreen_deviceLockedWithSecureAuthMethod_switchesToBouncer() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) - utils.authenticationRepository.setUnlocked(false) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - - underTest.dismissLockscreen() - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - } - - @Test - fun dismissLockScreen_deviceUnlocked_switchesToGone() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) - utils.authenticationRepository.setUnlocked(true) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - - underTest.dismissLockscreen() - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - - @Test - fun dismissLockScreen_deviceLockedWithInsecureAuthMethod_switchesToGone() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) - utils.authenticationRepository.setUnlocked(false) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - - underTest.dismissLockscreen() - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - - @Test - fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() = - testScope.runTest { - val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) - sceneInteractor.setCurrentScene(SceneModel(SceneKey.Lockscreen), "reason") - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - assertThat(isUnlocked).isFalse() - - sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone), "reason") - - assertThat(isUnlocked).isFalse() - } - - @Test - fun switchFromNonLockScreenToGone_authMethodSwipe_doesNotUnlockDevice() = - testScope.runTest { - val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) - runCurrent() - sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade), "reason") - runCurrent() - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) - runCurrent() - assertThat(isUnlocked).isFalse() - - sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone), "reason") - - assertThat(isUnlocked).isFalse() - } - - @Test - fun authMethodChangedToNone_notOnLockScreenScene_doesNotDismissLockScreen() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) - runCurrent() - sceneInteractor.setCurrentScene(SceneModel(SceneKey.QuickSettings), "reason") - runCurrent() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.QuickSettings)) - - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.QuickSettings)) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt index 66631dc9977b..addb1815cead 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt @@ -17,6 +17,8 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -26,15 +28,17 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAr import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection +import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(JUnit4::class) +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) @SmallTest class DefaultKeyguardBlueprintTest : SysuiTestCase() { private lateinit var underTest: DefaultKeyguardBlueprint @@ -45,6 +49,8 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { @Mock private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection @Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection + @Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection + @Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines @Before fun setup() { @@ -57,6 +63,8 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { defaultShortcutsSection, defaultAmbientIndicationAreaSection, defaultSettingsPopupMenuSection, + defaultStatusViewSection, + splitShadeGuidelines, ) } @@ -69,5 +77,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { verify(defaultShortcutsSection).apply(cs) verify(defaultAmbientIndicationAreaSection).apply(cs) verify(defaultSettingsPopupMenuSection).apply(cs) + verify(defaultStatusViewSection).apply(cs) + verify(splitShadeGuidelines).apply(cs) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt index 1444f8df4e4c..379c03c4353d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt @@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController +import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 63ee240fd2c6..834b9c526669 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils @@ -49,14 +49,11 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { private val underTest = LockscreenSceneViewModel( applicationScope = testScope.backgroundScope, - interactor = - utils.lockScreenSceneInteractor( + authenticationInteractor = authenticationInteractor, + bouncerInteractor = + utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, - bouncerInteractor = - utils.bouncerInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ), + sceneInteractor = sceneInteractor, ), ) @@ -87,17 +84,18 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { } @Test - fun upTransitionSceneKey_swipeToUnlockedEnabled_gone() = + fun upTransitionSceneKey_swipeToUnlockEnabled_gone() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setLockscreenEnabled(true) utils.authenticationRepository.setUnlocked(false) assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) } @Test - fun upTransitionSceneKey_swipeToUnlockedNotEnabled_bouncer() = + fun upTransitionSceneKey_swipeToUnlockNotEnabled_bouncer() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index ee42a7011264..bb365d05e9e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.qs.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -46,14 +46,10 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { private val underTest = QuickSettingsSceneViewModel( - lockscreenSceneInteractor = - utils.lockScreenSceneInteractor( + bouncerInteractor = + utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, - bouncerInteractor = - utils.bouncerInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ), + sceneInteractor = sceneInteractor, ), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 6be19b99dd3b..bec0b77e6480 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -21,6 +21,7 @@ package com.android.systemui.scene.domain.startable import android.view.Display import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.WakeSleepReason @@ -245,93 +246,110 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test - fun switchToGoneWhenDeviceSleepsUnlocked_featureEnabled() = + fun switchToLockscreenWhenDeviceSleepsLocked_featureEnabled() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key }) prepareState( isFeatureEnabled = true, - isDeviceUnlocked = true, + isDeviceUnlocked = false, initialSceneKey = SceneKey.Shade, ) assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) underTest.start() - keyguardRepository.setWakefulnessModel(ASLEEP) + keyguardRepository.setWakefulnessModel(STARTING_TO_SLEEP) - assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } @Test - fun switchToGoneWhenDeviceSleepsUnlocked_featureDisabled() = + fun switchToLockscreenWhenDeviceSleepsLocked_featureDisabled() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key }) prepareState( isFeatureEnabled = false, - isDeviceUnlocked = true, + isDeviceUnlocked = false, initialSceneKey = SceneKey.Shade, ) assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) underTest.start() - keyguardRepository.setWakefulnessModel(ASLEEP) + keyguardRepository.setWakefulnessModel(STARTING_TO_SLEEP) assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) } @Test - fun switchToLockscreenWhenDeviceSleepsLocked_featureEnabled() = + fun hydrateSystemUiState() = + testScope.runTest { + underTest.start() + runCurrent() + clearInvocations(sysUiState) + + listOf( + SceneKey.Gone, + SceneKey.Lockscreen, + SceneKey.Bouncer, + SceneKey.Shade, + SceneKey.QuickSettings, + ) + .forEachIndexed { index, sceneKey -> + sceneInteractor.setCurrentScene(SceneModel(sceneKey), "reason") + runCurrent() + + verify(sysUiState, times(index + 1)).commitUpdate(Display.DEFAULT_DISPLAY) + } + } + + @Test + fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone_featureEnabled() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key }) prepareState( isFeatureEnabled = true, - isDeviceUnlocked = false, - initialSceneKey = SceneKey.Shade, + initialSceneKey = SceneKey.Lockscreen, + authenticationMethod = AuthenticationMethodModel.None, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - keyguardRepository.setWakefulnessModel(ASLEEP) + keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE) - assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) } @Test - fun switchToLockscreenWhenDeviceSleepsLocked_featureDisabled() = + fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNotNone_featureEnabled() = testScope.runTest { val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key }) prepareState( - isFeatureEnabled = false, - isDeviceUnlocked = false, - initialSceneKey = SceneKey.Shade, + isFeatureEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + authenticationMethod = AuthenticationMethodModel.Pin, ) - assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - keyguardRepository.setWakefulnessModel(ASLEEP) + keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE) - assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } @Test - fun hydrateSystemUiState() = + fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone_featureDisabled() = testScope.runTest { + val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key }) + prepareState( + isFeatureEnabled = false, + initialSceneKey = SceneKey.Lockscreen, + authenticationMethod = AuthenticationMethodModel.None, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - runCurrent() - clearInvocations(sysUiState) - listOf( - SceneKey.Gone, - SceneKey.Lockscreen, - SceneKey.Bouncer, - SceneKey.Shade, - SceneKey.QuickSettings, - ) - .forEachIndexed { index, sceneKey -> - sceneInteractor.setCurrentScene(SceneModel(sceneKey), "reason") - runCurrent() + keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE) - verify(sysUiState, times(index + 1)).commitUpdate(Display.DEFAULT_DISPLAY) - } + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } private fun prepareState( @@ -339,17 +357,30 @@ class SceneContainerStartableTest : SysuiTestCase() { isDeviceUnlocked: Boolean = false, isBypassEnabled: Boolean = false, initialSceneKey: SceneKey? = null, + authenticationMethod: AuthenticationMethodModel? = null, ) { featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled) authenticationRepository.setUnlocked(isDeviceUnlocked) keyguardRepository.setBypassEnabled(isBypassEnabled) initialSceneKey?.let { sceneInteractor.setCurrentScene(SceneModel(it), "reason") } + authenticationMethod?.let { + authenticationRepository.setAuthenticationMethod(authenticationMethod) + authenticationRepository.setLockscreenEnabled( + authenticationMethod != AuthenticationMethodModel.None + ) + } } companion object { - private val ASLEEP = + private val STARTING_TO_SLEEP = + WakefulnessModel( + state = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.POWER_BUTTON + ) + private val STARTING_TO_WAKE = WakefulnessModel( - state = WakefulnessState.ASLEEP, + state = WakefulnessState.STARTING_TO_WAKE, lastWakeReason = WakeSleepReason.POWER_BUTTON, lastSleepReason = WakeSleepReason.POWER_BUTTON ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index c3540cfec72d..981e44bea846 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -424,6 +424,10 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); when(mView.findViewById(R.id.keyguard_status_view)) .thenReturn(mock(KeyguardStatusView.class)); + View rootView = mock(View.class); + when(mView.getRootView()).thenReturn(rootView); + when(rootView.findViewById(R.id.keyguard_status_view)) + .thenReturn(mock(KeyguardStatusView.class)); mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); mNotificationContainerParent.addView(keyguardStatusView); mNotificationContainerParent.onFinishInflate(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 8739b28c940e..d9301604c67f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.shade.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -47,14 +47,11 @@ class ShadeSceneViewModelTest : SysuiTestCase() { private val underTest = ShadeSceneViewModel( applicationScope = testScope.backgroundScope, - lockscreenSceneInteractor = - utils.lockScreenSceneInteractor( + authenticationInteractor = authenticationInteractor, + bouncerInteractor = + utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, - bouncerInteractor = - utils.bouncerInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ), + sceneInteractor = sceneInteractor, ), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt index 6306a36d9730..50ee6a31d389 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt @@ -55,6 +55,8 @@ class FakeMobileConnectionRepository( override val networkName = MutableStateFlow<NetworkNameModel>(NetworkNameModel.Default("default")) + override val isAllowedDuringAirplaneMode = MutableStateFlow(false) + fun setDataEnabled(enabled: Boolean) { _dataEnabled.value = enabled } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt index 441186acb6b7..a251c2850d41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt @@ -20,6 +20,7 @@ import android.telephony.TelephonyManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel @@ -319,6 +320,14 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { job.cancel() } + @Test + fun isAllowedDuringAirplaneMode_alwaysTrue() = + testScope.runTest { + val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode) + + assertThat(latest).isTrue() + } + private companion object { const val SUB_ID = 123 const val NET_ID = 456 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt index b701fbd66937..3dd2eaff7bce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt @@ -22,6 +22,7 @@ import android.telephony.TelephonyCallback import android.telephony.TelephonyManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel @@ -84,7 +85,11 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() { @Before fun setUp() { mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer) - carrierMergedRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer) + carrierMergedRepo = + FakeMobileConnectionRepository(SUB_ID, tableLogBuffer).apply { + // Mimicks the real carrier merged repository + this.isAllowedDuringAirplaneMode.value = true + } whenever( mobileFactory.build( @@ -300,6 +305,24 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() { } @Test + fun isAllowedDuringAirplaneMode_updatesWhenCarrierMergedUpdates() = + testScope.runTest { + initializeRepo(startingIsCarrierMerged = false) + + val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode) + + assertThat(latest).isFalse() + + underTest.setIsCarrierMerged(true) + + assertThat(latest).isTrue() + + underTest.setIsCarrierMerged(false) + + assertThat(latest).isFalse() + } + + @Test fun factory_reusesLogBuffersForSameConnection() = testScope.runTest { val realLoggerFactory = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt index cf832b4ab059..1ff737bfc137 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt @@ -53,6 +53,7 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN import androidx.test.filters.SmallTest import com.android.settingslib.mobile.MobileMappings import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState @@ -812,6 +813,14 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { job.cancel() } + @Test + fun isAllowedDuringAirplaneMode_alwaysFalse() = + testScope.runTest { + val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode) + + assertThat(latest).isFalse() + } + private inline fun <reified T> getTelephonyCallbackForType(): T { return MobileTelephonyHelpers.getTelephonyCallbackForType(telephonyManager) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt index c4e419366759..8d1da69d6877 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt @@ -77,6 +77,8 @@ class FakeMobileIconInteractor( override val isForceHidden = MutableStateFlow(false) + override val isAllowedDuringAirplaneMode = MutableStateFlow(false) + fun setIsEmergencyOnly(emergency: Boolean) { _isEmergencyOnly.value = emergency } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt index c2768654809b..58d3804b7155 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt @@ -23,6 +23,7 @@ import com.android.settingslib.mobile.MobileIconCarrierIdOverrides import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType @@ -473,6 +474,18 @@ class MobileIconInteractorTest : SysuiTestCase() { job.cancel() } + @Test + fun isAllowedDuringAirplaneMode_matchesRepo() = + testScope.runTest { + val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode) + + connectionRepository.isAllowedDuringAirplaneMode.value = true + assertThat(latest).isTrue() + + connectionRepository.isAllowedDuringAirplaneMode.value = false + assertThat(latest).isFalse() + } + private fun createInteractor( overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl() ) = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt index b5ab29d6217e..72feec78a979 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt @@ -116,12 +116,13 @@ class MobileIconViewModelTest : SysuiTestCase() { } @Test - fun isVisible_airplane_false() = + fun isVisible_airplaneAndNotAllowed_false() = testScope.runTest { var latest: Boolean? = null val job = underTest.isVisible.onEach { latest = it }.launchIn(this) airplaneModeRepository.setIsAirplaneMode(true) + interactor.isAllowedDuringAirplaneMode.value = false interactor.isForceHidden.value = false assertThat(latest).isFalse() @@ -129,6 +130,22 @@ class MobileIconViewModelTest : SysuiTestCase() { job.cancel() } + /** Regression test for b/291993542. */ + @Test + fun isVisible_airplaneButAllowed_true() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + + airplaneModeRepository.setIsAirplaneMode(true) + interactor.isAllowedDuringAirplaneMode.value = true + interactor.isForceHidden.value = false + + assertThat(latest).isTrue() + + job.cancel() + } + @Test fun isVisible_forceHidden_false() = testScope.runTest { @@ -157,7 +174,7 @@ class MobileIconViewModelTest : SysuiTestCase() { airplaneModeRepository.setIsAirplaneMode(true) assertThat(latest).isFalse() - airplaneModeRepository.setIsAirplaneMode(false) + interactor.isAllowedDuringAirplaneMode.value = true assertThat(latest).isTrue() interactor.isForceHidden.value = true diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index c2e1ac70af80..7c98df6c6317 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -20,7 +20,8 @@ import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternView import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel.SecurityMode -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.data.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import kotlinx.coroutines.flow.MutableStateFlow @@ -47,7 +48,7 @@ class FakeAuthenticationRepository( private val _authenticationMethod = MutableStateFlow<AuthenticationMethodModel>(DEFAULT_AUTHENTICATION_METHOD) - val authenticationMethod: StateFlow<AuthenticationMethodModel> = + override val authenticationMethod: StateFlow<AuthenticationMethodModel> = _authenticationMethod.asStateFlow() private var isLockscreenEnabled = true @@ -154,13 +155,13 @@ class FakeAuthenticationRepository( val DEFAULT_AUTHENTICATION_METHOD = AuthenticationMethodModel.Pin val PATTERN = listOf( - AuthenticationMethodModel.Pattern.PatternCoordinate(2, 0), - AuthenticationMethodModel.Pattern.PatternCoordinate(2, 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(2, 2), - AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(0, 0), - AuthenticationMethodModel.Pattern.PatternCoordinate(0, 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(0, 2), + AuthenticationPatternCoordinate(2, 0), + AuthenticationPatternCoordinate(2, 1), + AuthenticationPatternCoordinate(2, 2), + AuthenticationPatternCoordinate(1, 1), + AuthenticationPatternCoordinate(0, 0), + AuthenticationPatternCoordinate(0, 1), + AuthenticationPatternCoordinate(0, 2), ) const val MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING = 5 const val THROTTLE_DURATION_MS = 30000 @@ -172,7 +173,6 @@ class FakeAuthenticationRepository( is AuthenticationMethodModel.Pin -> SecurityMode.PIN is AuthenticationMethodModel.Password -> SecurityMode.Password is AuthenticationMethodModel.Pattern -> SecurityMode.Pattern - is AuthenticationMethodModel.Swipe, is AuthenticationMethodModel.None -> SecurityMode.None } } @@ -208,8 +208,7 @@ class FakeAuthenticationRepository( } } - private fun List<AuthenticationMethodModel.Pattern.PatternCoordinate>.toCells(): - List<LockPatternView.Cell> { + private fun List<AuthenticationPatternCoordinate>.toCells(): List<LockPatternView.Cell> { return map { coordinate -> LockPatternView.Cell.of(coordinate.y, coordinate.x) } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 15ce055bcc63..faebcaac1be3 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -146,7 +146,6 @@ class FakeKeyguardRepository : KeyguardRepository { _animateBottomAreaDozingTransitions.tryEmit(animate) } - @Deprecated("Deprecated as part of b/278057014") override fun setBottomAreaAlpha(alpha: Float) { _bottomAreaAlpha.value = alpha @@ -257,8 +256,11 @@ class FakeKeyguardRepository : KeyguardRepository { goingToFullShade: Boolean, occlusionTransitionRunning: Boolean ) { - _keyguardRootViewVisibility.value = KeyguardRootViewVisibilityState( - statusBarState, goingToFullShade, occlusionTransitionRunning - ) + _keyguardRootViewVisibility.value = + KeyguardRootViewVisibilityState( + statusBarState, + goingToFullShade, + occlusionTransitionRunning + ) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 6cffb669d466..62087df8c238 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -32,7 +32,6 @@ import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.keyguard.shared.model.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState @@ -176,23 +175,14 @@ class SceneTestUtils( fun bouncerViewModel( bouncerInteractor: BouncerInteractor, + authenticationInteractor: AuthenticationInteractor, ): BouncerViewModel { return BouncerViewModel( applicationContext = context, applicationScope = applicationScope(), - interactor = bouncerInteractor, - featureFlags = featureFlags, - ) - } - - fun lockScreenSceneInteractor( - authenticationInteractor: AuthenticationInteractor, - bouncerInteractor: BouncerInteractor, - ): LockscreenSceneInteractor { - return LockscreenSceneInteractor( - applicationScope = applicationScope(), - authenticationInteractor = authenticationInteractor, bouncerInteractor = bouncerInteractor, + authenticationInteractor = authenticationInteractor, + featureFlags = featureFlags, ) } diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index 0af9b2b4c26a..08a2c8561bd7 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -88,11 +88,14 @@ "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"], "name": "FrameworksServicesTests", "options": [ - { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }, - { "include-filter": "com.android.server.power.stats.BatteryStatsTests" } + { "include-filter": "com.android.server.am.BatteryStatsServiceTest" } ] }, { + "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"], + "name": "PowerStatsTests" + }, + { "file_patterns": ["Broadcast.*"], "name": "FrameworksMockingServicesTests", "options": [ diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java index 683b3eb7a92e..247094f2f796 100644 --- a/services/core/java/com/android/server/audio/AdiDeviceState.java +++ b/services/core/java/com/android/server/audio/AdiDeviceState.java @@ -16,6 +16,7 @@ package com.android.server.audio; +import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN; import static android.media.AudioSystem.DEVICE_NONE; import static android.media.AudioSystem.isBluetoothDevice; @@ -23,8 +24,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; +import android.media.AudioManager; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import java.util.Objects; @@ -41,8 +44,16 @@ import java.util.Objects; private final int mDeviceType; private final int mInternalDeviceType; + @NonNull private final String mDeviceAddress; + + /** Unique device id from internal device type and address. */ + private final Pair<Integer, String> mDeviceId; + + @AudioManager.AudioDeviceCategory + private int mAudioDeviceCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN; + private boolean mSAEnabled; private boolean mHasHeadTracker = false; private boolean mHeadTrackerEnabled; @@ -68,6 +79,12 @@ import java.util.Objects; } mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull( address) : ""; + + mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress); + } + + public Pair<Integer, String> getDeviceId() { + return mDeviceId; } @AudioDeviceInfo.AudioDeviceType @@ -109,6 +126,15 @@ import java.util.Objects; return mHasHeadTracker; } + @AudioDeviceInfo.AudioDeviceType + public int getAudioDeviceCategory() { + return mAudioDeviceCategory; + } + + public void setAudioDeviceCategory(@AudioDeviceInfo.AudioDeviceType int audioDeviceCategory) { + mAudioDeviceCategory = audioDeviceCategory; + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -127,20 +153,23 @@ import java.util.Objects; && mDeviceAddress.equals(sads.mDeviceAddress) // NonNull && mSAEnabled == sads.mSAEnabled && mHasHeadTracker == sads.mHasHeadTracker - && mHeadTrackerEnabled == sads.mHeadTrackerEnabled; + && mHeadTrackerEnabled == sads.mHeadTrackerEnabled + && mAudioDeviceCategory == sads.mAudioDeviceCategory; } @Override public int hashCode() { return Objects.hash(mDeviceType, mInternalDeviceType, mDeviceAddress, mSAEnabled, - mHasHeadTracker, mHeadTrackerEnabled); + mHasHeadTracker, mHeadTrackerEnabled, mAudioDeviceCategory); } @Override public String toString() { return "type: " + mDeviceType + "internal type: " + mInternalDeviceType - + " addr: " + mDeviceAddress + " enabled: " + mSAEnabled - + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled; + + " addr: " + mDeviceAddress + " bt audio type: " + + AudioManager.audioDeviceCategoryToString(mAudioDeviceCategory) + + " enabled: " + mSAEnabled + " HT: " + mHasHeadTracker + + " HTenabled: " + mHeadTrackerEnabled; } public String toPersistableString() { @@ -150,6 +179,7 @@ import java.util.Objects; .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mInternalDeviceType) + .append(SETTING_FIELD_SEPARATOR).append(mAudioDeviceCategory) .toString()); } @@ -174,21 +204,27 @@ import java.util.Objects; String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR); // we may have 5 fields for the legacy AdiDeviceState and 6 containing the internal // device type - if (fields.length != 5 && fields.length != 6) { - // expecting all fields, fewer may mean corruption, ignore those settings + if (fields.length < 5 || fields.length > 7) { + // different number of fields may mean corruption, ignore those settings + // newly added fields are optional (mInternalDeviceType, mBtAudioDeviceCategory) return null; } try { final int deviceType = Integer.parseInt(fields[0]); int internalDeviceType = -1; - if (fields.length == 6) { + if (fields.length >= 6) { internalDeviceType = Integer.parseInt(fields[5]); } + int audioDeviceCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN; + if (fields.length == 7) { + audioDeviceCategory = Integer.parseInt(fields[6]); + } final AdiDeviceState deviceState = new AdiDeviceState(deviceType, internalDeviceType, fields[1]); deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1); deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1); deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1); + deviceState.setAudioDeviceCategory(audioDeviceCategory); return deviceState; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e); @@ -200,5 +236,4 @@ import java.util.Objects; return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, mDeviceType, mDeviceAddress); } - } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 946f01688e66..af8aa9167217 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -63,6 +63,7 @@ import com.android.server.utils.EventLogger; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -2493,13 +2494,13 @@ public class AudioDeviceBroker { void onPersistAudioDeviceSettings() { final String deviceSettings = mDeviceInventory.getDeviceSettings(); - Log.v(TAG, "saving audio device settings: " + deviceSettings); + Log.v(TAG, "saving AdiDeviceState: " + deviceSettings); final SettingsAdapter settings = mAudioService.getSettings(); boolean res = settings.putSecureStringForUser(mAudioService.getContentResolver(), Settings.Secure.AUDIO_DEVICE_INVENTORY, deviceSettings, UserHandle.USER_CURRENT); if (!res) { - Log.e(TAG, "error saving audio device settings: " + deviceSettings); + Log.e(TAG, "error saving AdiDeviceState: " + deviceSettings); } } @@ -2509,7 +2510,7 @@ public class AudioDeviceBroker { String settings = settingsAdapter.getSecureStringForUser(contentResolver, Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT); if (settings == null) { - Log.i(TAG, "reading spatial audio device settings from legacy key" + Log.i(TAG, "reading AdiDeviceState from legacy key" + Settings.Secure.SPATIAL_AUDIO_ENABLED); // legacy string format for key SPATIAL_AUDIO_ENABLED has the same order of fields like // the strings for key AUDIO_DEVICE_INVENTORY. This will ensure to construct valid @@ -2517,21 +2518,21 @@ public class AudioDeviceBroker { settings = settingsAdapter.getSecureStringForUser(contentResolver, Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT); if (settings == null) { - Log.i(TAG, "no spatial audio device settings stored with legacy key"); + Log.i(TAG, "no AdiDeviceState stored with legacy key"); } else if (!settings.equals("")) { // Delete old key value and update the new key if (!settingsAdapter.putSecureStringForUser(contentResolver, Settings.Secure.SPATIAL_AUDIO_ENABLED, /*value=*/"", UserHandle.USER_CURRENT)) { - Log.w(TAG, "cannot erase the legacy audio device settings with key " + Log.w(TAG, "cannot erase the legacy AdiDeviceState with key " + Settings.Secure.SPATIAL_AUDIO_ENABLED); } if (!settingsAdapter.putSecureStringForUser(contentResolver, Settings.Secure.AUDIO_DEVICE_INVENTORY, settings, UserHandle.USER_CURRENT)) { - Log.e(TAG, "error updating the new audio device settings with key " + Log.e(TAG, "error updating the new AdiDeviceState with key " + Settings.Secure.AUDIO_DEVICE_INVENTORY); } } @@ -2551,19 +2552,29 @@ public class AudioDeviceBroker { return mDeviceInventory.getDeviceSettings(); } - List<AdiDeviceState> getImmutableDeviceInventory() { + Collection<AdiDeviceState> getImmutableDeviceInventory() { return mDeviceInventory.getImmutableDeviceInventory(); } - void addDeviceStateToInventory(AdiDeviceState deviceState) { - mDeviceInventory.addDeviceStateToInventory(deviceState); + void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) { + mDeviceInventory.addOrUpdateDeviceSAStateInInventory(deviceState); } + void addOrUpdateBtAudioDeviceCategoryInInventory(AdiDeviceState deviceState) { + mDeviceInventory.addOrUpdateAudioDeviceCategoryInInventory(deviceState); + } + + @Nullable AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada, int canonicalType) { return mDeviceInventory.findDeviceStateForAudioDeviceAttributes(ada, canonicalType); } + @Nullable + AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) { + return mDeviceInventory.findBtDeviceStateForAddress(address, isBle); + } + //------------------------------------------------ // for testing purposes only void clearDeviceInventory() { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index f5b7ecf5daf4..5a92cb464950 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -15,6 +15,8 @@ */ package com.android.server.audio; +import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP_SET; +import static android.media.AudioSystem.DEVICE_OUT_ALL_BLE_SET; import static android.media.AudioSystem.isBluetoothDevice; import android.annotation.NonNull; @@ -61,11 +63,13 @@ import com.google.android.collect.Sets; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -90,35 +94,95 @@ public class AudioDeviceInventory { private static final String mMetricsId = "audio.device."; private final Object mDeviceInventoryLock = new Object(); - @GuardedBy("mDeviceCatalogLock") - private final ArrayList<AdiDeviceState> mDeviceInventory = new ArrayList<>(0); - List<AdiDeviceState> getImmutableDeviceInventory() { + + @GuardedBy("mDeviceInventoryLock") + private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>(); + + Collection<AdiDeviceState> getImmutableDeviceInventory() { + synchronized (mDeviceInventoryLock) { + return mDeviceInventory.values(); + } + } + + /** + * Adds a new AdiDeviceState or updates the spatial audio related properties of the matching + * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list. + * @param deviceState the device to update + */ + void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) { + synchronized (mDeviceInventoryLock) { + mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, (oldState, newState) -> { + oldState.setHasHeadTracker(newState.hasHeadTracker()); + oldState.setHeadTrackerEnabled(newState.isHeadTrackerEnabled()); + oldState.setSAEnabled(newState.isSAEnabled()); + return oldState; + }); + } + } + + /** + * Adds a new AdiDeviceState or updates the audio device cateogory of the matching + * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list. + * @param deviceState the device to update + */ + void addOrUpdateAudioDeviceCategoryInInventory(AdiDeviceState deviceState) { synchronized (mDeviceInventoryLock) { - return List.copyOf(mDeviceInventory); + mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, (oldState, newState) -> { + oldState.setAudioDeviceCategory(newState.getAudioDeviceCategory()); + return oldState; + }); } } - void addDeviceStateToInventory(AdiDeviceState deviceState) { + /** + * Finds the BT device that matches the passed {@code address}. Currently, this method only + * returns a valid device for A2DP and BLE devices. + * + * @param address MAC address of BT device + * @param isBle true if the device is BLE, false for A2DP + * @return the found {@link AdiDeviceState} or {@code null} otherwise. + */ + @Nullable + AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) { synchronized (mDeviceInventoryLock) { - mDeviceInventory.add(deviceState); + final Set<Integer> deviceSet = isBle ? DEVICE_OUT_ALL_BLE_SET : DEVICE_OUT_ALL_A2DP_SET; + for (Integer internalType : deviceSet) { + AdiDeviceState deviceState = mDeviceInventory.get( + new Pair<>(internalType, address)); + if (deviceState != null) { + return deviceState; + } + } } + return null; } + /** + * Finds the device state that matches the passed {@link AudioDeviceAttributes} and device + * type. Note: currently this method only returns a valid device for A2DP and BLE devices. + * + * @param ada attributes of device to match + * @param canonicalDeviceType external device type to match + * @return the found {@link AdiDeviceState} matching a cached A2DP or BLE device or + * {@code null} otherwise. + */ + @Nullable AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada, int canonicalDeviceType) { final boolean isWireless = isBluetoothDevice(ada.getInternalType()); synchronized (mDeviceInventoryLock) { - for (AdiDeviceState deviceSetting : mDeviceInventory) { - if (deviceSetting.getDeviceType() == canonicalDeviceType + for (AdiDeviceState deviceState : mDeviceInventory.values()) { + if (deviceState.getDeviceType() == canonicalDeviceType && (!isWireless || ada.getAddress().equals( - deviceSetting.getDeviceAddress()))) { - return deviceSetting; + deviceState.getDeviceAddress()))) { + return deviceState; } } } return null; } + /** Clears all cached {@link AdiDeviceState}'s. */ void clearDeviceInventory() { synchronized (mDeviceInventoryLock) { mDeviceInventory.clear(); @@ -384,7 +448,7 @@ public class AudioDeviceInventory { + " role:" + key.second + " devices:" + devices); }); pw.println("\ndevices:\n"); synchronized (mDeviceInventoryLock) { - for (AdiDeviceState device : mDeviceInventory) { + for (AdiDeviceState device : mDeviceInventory.values()) { pw.println("\t" + device + "\n"); } } @@ -1232,11 +1296,11 @@ public class AudioDeviceInventory { AudioDeviceInfo[] connectedDevices = AudioManager.getDevicesStatic( AudioManager.GET_DEVICES_ALL); - Iterator<Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole = + Iterator<Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole = rolesMap.entrySet().iterator(); while (itRole.hasNext()) { - Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry = + Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry = itRole.next(); Pair<Integer, Integer> keyRole = entry.getKey(); Iterator<AudioDeviceAttributes> itDev = rolesMap.get(keyRole).iterator(); @@ -2423,19 +2487,20 @@ public class AudioDeviceInventory { int deviceCatalogSize = 0; synchronized (mDeviceInventoryLock) { deviceCatalogSize = mDeviceInventory.size(); - } - final StringBuilder settingsBuilder = new StringBuilder( - deviceCatalogSize * AdiDeviceState.getPeristedMaxSize()); - synchronized (mDeviceInventoryLock) { - for (int i = 0; i < mDeviceInventory.size(); i++) { - settingsBuilder.append(mDeviceInventory.get(i).toPersistableString()); - if (i != mDeviceInventory.size() - 1) { - settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR); - } + final StringBuilder settingsBuilder = new StringBuilder( + deviceCatalogSize * AdiDeviceState.getPeristedMaxSize()); + + Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator(); + if (iterator.hasNext()) { + settingsBuilder.append(iterator.next().toPersistableString()); + } + while (iterator.hasNext()) { + settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR); + settingsBuilder.append(iterator.next().toPersistableString()); } + return settingsBuilder.toString(); } - return settingsBuilder.toString(); } /*package*/ void setDeviceSettings(String settings) { @@ -2448,7 +2513,8 @@ public class AudioDeviceInventory { // Note if the device is not compatible with spatialization mode or the device // type is not canonical, it will be ignored in {@link SpatializerHelper}. if (devState != null) { - addDeviceStateToInventory(devState); + addOrUpdateDeviceSAStateInInventory(devState); + addOrUpdateAudioDeviceCategoryInInventory(devState); } } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 3353b9ec538f..76c4cfe929bb 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -18,6 +18,14 @@ package com.android.server.audio; import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED; import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT; +import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET; +import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER; +import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP; +import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES; +import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN; +import static android.media.AudioManager.DEVICE_OUT_BLE_HEADSET; +import static android.media.AudioManager.DEVICE_OUT_BLE_SPEAKER; +import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_A2DP; import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; @@ -92,6 +100,7 @@ import android.media.AudioFocusRequest; import android.media.AudioFormat; import android.media.AudioHalVersionInfo; import android.media.AudioManager; +import android.media.AudioManager.AudioDeviceCategory; import android.media.AudioManagerInternal; import android.media.AudioMixerAttributes; import android.media.AudioPlaybackConfiguration; @@ -405,6 +414,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_DISABLE_AUDIO_FOR_UID = 100; private static final int MSG_INIT_STREAMS_VOLUMES = 101; private static final int MSG_INIT_SPATIALIZER = 102; + private static final int MSG_INIT_ADI_DEVICE_STATES = 103; // end of messages handled under wakelock @@ -1286,6 +1296,8 @@ public class AudioService extends IAudioService.Stub // done with service initialization, continue additional work in our Handler thread queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES, 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */); + queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_ADI_DEVICE_STATES, + 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */); queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER, 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */); @@ -7377,7 +7389,7 @@ public class AudioService extends IAudioService.Stub if (pkgName == null) { pkgName = ""; } - if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) { + if (device.getType() == TYPE_BLUETOOTH_A2DP) { avrcpSupportsAbsoluteVolume(device.getAddress(), deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE); return; @@ -9253,6 +9265,11 @@ public class AudioService extends IAudioService.Stub mAudioEventWakeLock.release(); break; + case MSG_INIT_ADI_DEVICE_STATES: + onInitAdiDeviceStates(); + mAudioEventWakeLock.release(); + break; + case MSG_INIT_SPATIALIZER: onInitSpatializer(); mAudioEventWakeLock.release(); @@ -10322,8 +10339,13 @@ public class AudioService extends IAudioService.Stub /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0); } - void onInitSpatializer() { + void onInitAdiDeviceStates() { mDeviceBroker.onReadAudioDeviceSettings(); + mSoundDoseHelper.initCachedAudioDeviceCategories( + mDeviceBroker.getImmutableDeviceInventory()); + } + + void onInitSpatializer() { mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect); mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect); } @@ -10718,6 +10740,51 @@ public class AudioService extends IAudioService.Stub return mSoundDoseHelper.isCsdEnabled(); } + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public void setBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle, + @AudioDeviceCategory int btAudioDeviceCategory) { + super.setBluetoothAudioDeviceCategory_enforcePermission(); + + final String addr = Objects.requireNonNull(address); + + AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(addr, isBle); + + int internalType = !isBle ? DEVICE_OUT_BLUETOOTH_A2DP + : ((btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES) + ? DEVICE_OUT_BLE_HEADSET : DEVICE_OUT_BLE_SPEAKER); + int deviceType = !isBle ? TYPE_BLUETOOTH_A2DP + : ((btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES) ? TYPE_BLE_HEADSET + : TYPE_BLE_SPEAKER); + + if (deviceState == null) { + deviceState = new AdiDeviceState(deviceType, internalType, addr); + } + + deviceState.setAudioDeviceCategory(btAudioDeviceCategory); + + mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState); + mDeviceBroker.persistAudioDeviceSettings(); + + mSoundDoseHelper.setAudioDeviceCategory(addr, internalType, + btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES); + } + + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + @AudioDeviceCategory + public int getBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle) { + super.getBluetoothAudioDeviceCategory_enforcePermission(); + + final AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress( + Objects.requireNonNull(address), isBle); + if (deviceState == null) { + return AUDIO_DEVICE_CATEGORY_UNKNOWN; + } + + return deviceState.getAudioDeviceCategory(); + } + //========================================================================================== // Hdmi CEC: // - System audio mode: diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 01af3a838597..851c5c3cb73c 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -16,6 +16,9 @@ package com.android.server.audio; +import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES; +import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN; + import static com.android.server.audio.AudioService.MAX_STREAM_VOLUME; import static com.android.server.audio.AudioService.MIN_STREAM_VOLUME; import static com.android.server.audio.AudioService.MSG_SET_DEVICE_VOLUME; @@ -57,6 +60,7 @@ import com.android.server.utils.EventLogger; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -189,6 +193,9 @@ public class SoundDoseHelper { private final AtomicBoolean mEnableCsd = new AtomicBoolean(false); + private ArrayList<ISoundDose.AudioDeviceCategory> mCachedAudioDeviceCategories = + new ArrayList<>(); + private final Object mCsdStateLock = new Object(); private final AtomicReference<ISoundDose> mSoundDose = new AtomicReference<>(); @@ -487,6 +494,43 @@ public class SoundDoseHelper { return false; } + void setAudioDeviceCategory(String address, int internalAudioType, boolean isHeadphone) { + if (!mEnableCsd.get()) { + return; + } + + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Sound dose interface not initialized"); + return; + } + + try { + final ISoundDose.AudioDeviceCategory audioDeviceCategory = + new ISoundDose.AudioDeviceCategory(); + audioDeviceCategory.address = address; + audioDeviceCategory.internalAudioType = internalAudioType; + audioDeviceCategory.csdCompatible = isHeadphone; + soundDose.setAudioDeviceCategory(audioDeviceCategory); + } catch (RemoteException e) { + Log.e(TAG, "Exception while forcing the internal MEL computation", e); + } + } + + void initCachedAudioDeviceCategories(Collection<AdiDeviceState> deviceStates) { + for (final AdiDeviceState state : deviceStates) { + if (state.getAudioDeviceCategory() != AUDIO_DEVICE_CATEGORY_UNKNOWN) { + final ISoundDose.AudioDeviceCategory audioDeviceCategory = + new ISoundDose.AudioDeviceCategory(); + audioDeviceCategory.address = state.getDeviceAddress(); + audioDeviceCategory.internalAudioType = state.getInternalDeviceType(); + audioDeviceCategory.csdCompatible = + state.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_HEADPHONES; + mCachedAudioDeviceCategories.add(audioDeviceCategory); + } + } + } + /*package*/ int safeMediaVolumeIndex(int device) { final int vol = mSafeMediaVolumeDevices.get(device); if (vol == SAFE_MEDIA_VOLUME_UNINITIALIZED) { @@ -810,6 +854,16 @@ public class SoundDoseHelper { Log.v(TAG, "Initializing sound dose"); + try { + if (mCachedAudioDeviceCategories.size() > 0) { + soundDose.initCachedAudioDeviceCategories(mCachedAudioDeviceCategories.toArray( + new ISoundDose.AudioDeviceCategory[0])); + mCachedAudioDeviceCategories.clear(); + } + } catch (RemoteException e) { + Log.e(TAG, "Exception while forcing the internal MEL computation", e); + } + synchronized (mCsdStateLock) { if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) { mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L; diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 969dd60a8012..496bdf48b5bf 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -560,7 +560,7 @@ public class SpatializerHelper { updatedDevice = new AdiDeviceState(canonicalDeviceType, ada.getInternalType(), ada.getAddress()); initSAState(updatedDevice); - mDeviceBroker.addDeviceStateToInventory(updatedDevice); + mDeviceBroker.addOrUpdateDeviceSAStateInInventory(updatedDevice); } if (updatedDevice != null) { onRoutingUpdated(); @@ -693,7 +693,7 @@ public class SpatializerHelper { new AdiDeviceState(canonicalDeviceType, ada.getInternalType(), ada.getAddress()); initSAState(deviceState); - mDeviceBroker.addDeviceStateToInventory(deviceState); + mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState); mDeviceBroker.persistAudioDeviceSettings(); logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later. } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 3d0ea9d8bef6..54d1faa39be0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -260,6 +260,14 @@ class FingerprintAuthenticationClient final AidlSession session = getFreshDaemon(); final OperationContextExt opContext = getOperationContext(); + final ICancellationSignal cancel; + if (session.hasContextMethods()) { + cancel = session.getSession().authenticateWithContext( + mOperationId, opContext.toAidlContext(getOptions())); + } else { + cancel = session.getSession().authenticate(mOperationId); + } + getBiometricContext().subscribe(opContext, ctx -> { if (session.hasContextMethods()) { try { @@ -281,12 +289,7 @@ class FingerprintAuthenticationClient mALSProbeCallback.getProbe().enable(); } - if (session.hasContextMethods()) { - return session.getSession().authenticateWithContext( - mOperationId, opContext.toAidlContext(getOptions())); - } else { - return session.getSession().authenticate(mOperationId); - } + return cancel; } @Override diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index e5965eff7070..51a0ef6af73b 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -1568,37 +1568,40 @@ public class DisplayDeviceConfig { public String toString() { return "DisplayDeviceConfig{" + "mLoadedFrom=" + mLoadedFrom - + ", mBacklight=" + Arrays.toString(mBacklight) + + "\n" + + "mBacklight=" + Arrays.toString(mBacklight) + ", mNits=" + Arrays.toString(mNits) + ", mRawBacklight=" + Arrays.toString(mRawBacklight) + ", mRawNits=" + Arrays.toString(mRawNits) + ", mInterpolationType=" + mInterpolationType - + ", mBrightness=" + Arrays.toString(mBrightness) - + ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline + + "mBrightness=" + Arrays.toString(mBrightness) + + "\n" + + "mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline + ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline + ", mNitsToBacklightSpline=" + mNitsToBacklightSpline + ", mBacklightMinimum=" + mBacklightMinimum + ", mBacklightMaximum=" + mBacklightMaximum + ", mBrightnessDefault=" + mBrightnessDefault + ", mQuirks=" + mQuirks - + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled - + ", mLuxThrottlingData=" + mLuxThrottlingData + + ", mIsHighBrightnessModeEnabled=" + mIsHighBrightnessModeEnabled + + "\n" + + "mLuxThrottlingData=" + mLuxThrottlingData + ", mHbmData=" + mHbmData + ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline + ", mThermalBrightnessThrottlingDataMapByThrottlingId=" + mThermalBrightnessThrottlingDataMapByThrottlingId + "\n" - + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease + + "mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease + ", mBrightnessRampDecreaseMaxMillis=" + mBrightnessRampDecreaseMaxMillis + ", mBrightnessRampIncreaseMaxMillis=" + mBrightnessRampIncreaseMaxMillis + "\n" - + ", mAmbientHorizonLong=" + mAmbientHorizonLong + + "mAmbientHorizonLong=" + mAmbientHorizonLong + ", mAmbientHorizonShort=" + mAmbientHorizonShort + "\n" - + ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold + + "mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold + ", mScreenDarkeningMinThresholdIdle=" + mScreenDarkeningMinThresholdIdle + ", mScreenBrighteningMinThreshold=" + mScreenBrighteningMinThreshold + ", mScreenBrighteningMinThresholdIdle=" + mScreenBrighteningMinThresholdIdle @@ -1608,7 +1611,7 @@ public class DisplayDeviceConfig { + ", mAmbientLuxBrighteningMinThresholdIdle=" + mAmbientLuxBrighteningMinThresholdIdle + "\n" - + ", mScreenBrighteningLevels=" + Arrays.toString( + + "mScreenBrighteningLevels=" + Arrays.toString( mScreenBrighteningLevels) + ", mScreenBrighteningPercentages=" + Arrays.toString( mScreenBrighteningPercentages) @@ -1625,7 +1628,7 @@ public class DisplayDeviceConfig { + ", mAmbientDarkeningPercentages=" + Arrays.toString( mAmbientDarkeningPercentages) + "\n" - + ", mAmbientBrighteningLevelsIdle=" + Arrays.toString( + + "mAmbientBrighteningLevelsIdle=" + Arrays.toString( mAmbientBrighteningLevelsIdle) + ", mAmbientBrighteningPercentagesIdle=" + Arrays.toString( mAmbientBrighteningPercentagesIdle) @@ -1642,7 +1645,7 @@ public class DisplayDeviceConfig { + ", mScreenDarkeningPercentagesIdle=" + Arrays.toString( mScreenDarkeningPercentagesIdle) + "\n" - + ", mAmbientLightSensor=" + mAmbientLightSensor + + "mAmbientLightSensor=" + mAmbientLightSensor + ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor + ", mProximitySensor=" + mProximitySensor + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray()) @@ -1656,7 +1659,7 @@ public class DisplayDeviceConfig { + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable + ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable + "\n" - + ", mDefaultLowBlockingZoneRefreshRate= " + mDefaultLowBlockingZoneRefreshRate + + "mDefaultLowBlockingZoneRefreshRate= " + mDefaultLowBlockingZoneRefreshRate + ", mDefaultHighBlockingZoneRefreshRate= " + mDefaultHighBlockingZoneRefreshRate + ", mDefaultPeakRefreshRate= " + mDefaultPeakRefreshRate + ", mDefaultRefreshRate= " + mDefaultRefreshRate @@ -1665,7 +1668,7 @@ public class DisplayDeviceConfig { + ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight + ", mRefreshRateThrottlingMap= " + mRefreshRateThrottlingMap + "\n" - + ", mLowDisplayBrightnessThresholds= " + + "mLowDisplayBrightnessThresholds= " + Arrays.toString(mLowDisplayBrightnessThresholds) + ", mLowAmbientBrightnessThresholds= " + Arrays.toString(mLowAmbientBrightnessThresholds) @@ -1674,10 +1677,10 @@ public class DisplayDeviceConfig { + ", mHighAmbientBrightnessThresholds= " + Arrays.toString(mHighAmbientBrightnessThresholds) + "\n" - + ", mScreenOffBrightnessSensorValueToLux=" + Arrays.toString( + + "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString( mScreenOffBrightnessSensorValueToLux) + "\n" - + ", mUsiVersion= " + mHostUsiVersion + + "mUsiVersion= " + mHostUsiVersion + "}"; } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 19dffeba868e..58f4d0859c1a 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -39,6 +39,7 @@ import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.ROOT_UID; @@ -593,7 +594,7 @@ public final class DisplayManagerService extends SystemService { DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); publishBinderService(Context.DISPLAY_SERVICE, new BinderService(), - true /*allowIsolated*/); + true /*allowIsolated*/, DUMP_FLAG_PRIORITY_CRITICAL); publishLocalService(DisplayManagerInternal.class, new LocalService()); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index b96187f7af77..5213d3179de7 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -613,7 +613,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal setUpAutoBrightness(resources, handler); - mColorFadeEnabled = mInjector.isColorFadeEnabled(resources); + mColorFadeEnabled = mInjector.isColorFadeEnabled() + && !resources.getBoolean( + com.android.internal.R.bool.config_displayColorFadeDisabled); mColorFadeFadesConfig = resources.getBoolean( R.bool.config_animateScreenLights); @@ -2449,6 +2451,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal dumpRbcEvents(pw); + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.dump(pw); + } + if (mBrightnessRangeController != null) { mBrightnessRangeController.dump(pw); } @@ -3005,10 +3011,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal sensorManager, resources); } - boolean isColorFadeEnabled(Resources resources) { - return !ActivityManager.isLowRamDeviceStatic() - && !resources.getBoolean( - com.android.internal.R.bool.config_displayColorFadeDisabled); + boolean isColorFadeEnabled() { + return !ActivityManager.isLowRamDeviceStatic(); } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 75fcca5eec9a..1e6486a17fe7 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -18,7 +18,6 @@ package com.android.server.power; import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; -import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT; import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED; import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED; @@ -1280,7 +1279,7 @@ public final class PowerManagerService extends SystemService @Override public void onStart() { publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false, - DUMP_FLAG_PRIORITY_DEFAULT | DUMP_FLAG_PRIORITY_CRITICAL); + DUMP_FLAG_PRIORITY_CRITICAL); publishLocalService(PowerManagerInternal.class, mLocalService); Watchdog.getInstance().addMonitor(this); diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING index 8374997e3fa2..19086a184068 100644 --- a/services/core/java/com/android/server/power/TEST_MAPPING +++ b/services/core/java/com/android/server/power/TEST_MAPPING @@ -17,15 +17,6 @@ ] }, { - "name": "FrameworksServicesTests", - "options": [ - {"include-filter": "com.android.server.power"}, - {"exclude-filter": "com.android.server.power.BatteryStatsTests"}, - {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"} - ] - }, - { "name": "PowerServiceTests", "options": [ {"include-filter": "com.android.server.power"}, @@ -48,15 +39,13 @@ { "name": "FrameworksServicesTests", "options": [ - {"include-filter": "com.android.server.power"}, - {"exclude-filter": "com.android.server.power.BatteryStatsTests"} + {"include-filter": "com.android.server.power"} ] }, { "name": "PowerServiceTests", "options": [ {"include-filter": "com.android.server.power"}, - {"exclude-filter": "com.android.server.power.BatteryStatsTests"}, {"exclude-annotation": "org.junit.Ignore"} ] } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index e80cbb302424..9af12ad6e766 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1851,9 +1851,17 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { 0 /* launchFlags */); task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP); - // Set the windowing mode to undefined by default to let the root task inherited the - // windowing mode. - task.setWindowingMode(WINDOWING_MODE_UNDEFINED); + // If the task is going to be reparented to the non-fullscreen root TDA and the task + // is set to FULLSCREEN explicitly, we keep the windowing mode as is. Otherwise, the + // task will inherit the display windowing mode unexpectedly. + final boolean keepWindowingMode = launchRoot == null + && task.getRequestedOverrideWindowingMode() == WINDOWING_MODE_FULLSCREEN + && toDisplayArea.getWindowingMode() != WINDOWING_MODE_FULLSCREEN; + if (!keepWindowingMode) { + // Set the windowing mode to undefined to let the root task inherited the + // windowing mode. + task.setWindowingMode(WINDOWING_MODE_UNDEFINED); + } lastReparentedRootTask = task; } // Root task may be removed from this display. Ensure each root task will be processed diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index a84749aa6643..fe7a70a48e8d 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -382,18 +382,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } @Override - public int finishTransition(@NonNull IBinder transitionToken, - @Nullable WindowContainerTransaction t, - @Nullable IWindowContainerTransactionCallback callback) { + public void finishTransition(@NonNull IBinder transitionToken, + @Nullable WindowContainerTransaction t) { enforceTaskPermission("finishTransition()"); final CallerInfo caller = new CallerInfo(); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - int syncId = -1; - if (t != null && callback != null) { - syncId = startSyncWithOrganizer(callback); - } final Transition transition = Transition.fromBinder(transitionToken); // apply the incoming transaction before finish in case it alters the visibility // of the participants. @@ -402,14 +397,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // changes of the transition participants will only set visible-requested // and still let finishTransition handle the participants. mTransitionController.mFinishingTransition = transition; - applyTransaction(t, syncId, null /*transition*/, caller, transition); + applyTransaction(t, -1 /* syncId */, null /*transition*/, caller, transition); } mTransitionController.finishTransition(transition); mTransitionController.mFinishingTransition = null; - if (syncId >= 0) { - setSyncReady(syncId); - } - return syncId; } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java index 0b1e3386cbac..d6d5264257e6 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java @@ -141,7 +141,7 @@ import java.util.concurrent.atomic.AtomicReference; * Tests for {@link com.android.server.power.PowerManagerService}. * * Build/Install/Run: - * atest FrameworksServicesTests:PowerManagerServiceTest + * atest PowerServiceTests:PowerManagerServiceTest */ @SuppressWarnings("GuardedBy") @RunWith(TestParameterInjector.class) @@ -240,6 +240,10 @@ public class PowerManagerServiceTest { cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); when(mContextSpy.getContentResolver()).thenReturn(cr); + when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_dreamsSupported)) + .thenReturn(true); + when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_dreamsEnabledByDefault)) + .thenReturn(true); Settings.Global.putInt(mContextSpy.getContentResolver(), Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); Settings.Secure.putInt(mContextSpy.getContentResolver(), @@ -1084,13 +1088,9 @@ public class PowerManagerServiceTest { @SuppressWarnings("GuardedBy") @Test public void testScreensaverActivateOnSleepEnabled_powered_afterTimeout_goesToDreaming() { - when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_dreamsSupported)) - .thenReturn(true); when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(true); Settings.Secure.putInt(mContextSpy.getContentResolver(), Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1); - Settings.Secure.putInt(mContextSpy.getContentResolver(), - Settings.Secure.SCREENSAVER_ENABLED, 1); doAnswer(inv -> { when(mDreamManagerInternalMock.isDreaming()).thenReturn(true); @@ -1112,8 +1112,6 @@ public class PowerManagerServiceTest { public void testAmbientSuppression_disablesDreamingAndWakesDevice() { Settings.Secure.putInt(mContextSpy.getContentResolver(), Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1); - Settings.Secure.putInt(mContextSpy.getContentResolver(), - Settings.Secure.SCREENSAVER_ENABLED, 1); setDreamsDisabledByAmbientModeSuppressionConfig(true); setMinimumScreenOffTimeoutConfig(10000); @@ -1141,8 +1139,6 @@ public class PowerManagerServiceTest { public void testAmbientSuppressionDisabled_shouldNotWakeDevice() { Settings.Secure.putInt(mContextSpy.getContentResolver(), Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1); - Settings.Secure.putInt(mContextSpy.getContentResolver(), - Settings.Secure.SCREENSAVER_ENABLED, 1); setDreamsDisabledByAmbientModeSuppressionConfig(false); setMinimumScreenOffTimeoutConfig(10000); @@ -1169,8 +1165,6 @@ public class PowerManagerServiceTest { public void testAmbientSuppression_doesNotAffectDreamForcing() { Settings.Secure.putInt(mContextSpy.getContentResolver(), Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1); - Settings.Secure.putInt(mContextSpy.getContentResolver(), - Settings.Secure.SCREENSAVER_ENABLED, 1); setDreamsDisabledByAmbientModeSuppressionConfig(true); setMinimumScreenOffTimeoutConfig(10000); @@ -1196,8 +1190,6 @@ public class PowerManagerServiceTest { public void testBatteryDrainDuringDream() { Settings.Secure.putInt(mContextSpy.getContentResolver(), Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1); - Settings.Secure.putInt(mContextSpy.getContentResolver(), - Settings.Secure.SCREENSAVER_ENABLED, 1); setMinimumScreenOffTimeoutConfig(100); setDreamsBatteryLevelDrainConfig(5); diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp new file mode 100644 index 000000000000..05acd9b8eeb1 --- /dev/null +++ b/services/tests/powerstatstests/Android.bp @@ -0,0 +1,52 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "PowerStatsTests", + + // Include all test java files. + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "services.core", + "coretests-aidl", + "platformprotosnano", + "junit", + "truth-prebuilt", + "androidx.test.runner", + "androidx.test.ext.junit", + "androidx.test.ext.truth", + "androidx.test.uiautomator_uiautomator", + "mockito-target-minus-junit4", + "servicestests-utils", + ], + + libs: [ + "android.test.base", + ], + + resource_dirs: ["res/"], + + data: [ + ":BstatsTestApp", + ], + + test_suites: [ + "automotive-tests", + "device-tests", + ], + + platform_apis: true, + + certificate: "platform", + + dxflags: ["--multi-dex"], + + optimize: { + enabled: false, + }, +} diff --git a/services/tests/powerstatstests/AndroidManifest.xml b/services/tests/powerstatstests/AndroidManifest.xml new file mode 100644 index 000000000000..d3a88d2bc38c --- /dev/null +++ b/services/tests/powerstatstests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.powerstatstests"> + + <uses-permission android:name="android.permission.BATTERY_STATS" /> + <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> + <uses-permission android:name="android.permission.MANAGE_USERS"/> + + <queries> + <package android:name="com.android.coretests.apps.bstatstestapp" /> + </queries> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.powerstatstests" + android:label="BatteryStats and PowerStats Services Tests"/> +</manifest> diff --git a/services/tests/powerstatstests/AndroidTest.xml b/services/tests/powerstatstests/AndroidTest.xml new file mode 100644 index 000000000000..79b07e812b28 --- /dev/null +++ b/services/tests/powerstatstests/AndroidTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Power Stats Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="PowerStatsTests.apk" /> + <option name="test-file-name" value="BstatsTestApp.apk" /> + </target_preparer> + + <option name="test-tag" value="PowerStatsTests" /> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.powerstatstests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" /> + </test> +</configuration> diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/services/tests/powerstatstests/BstatsTestApp/Android.bp index c82da9e7b449..c82da9e7b449 100644 --- a/core/tests/coretests/BstatsTestApp/Android.bp +++ b/services/tests/powerstatstests/BstatsTestApp/Android.bp diff --git a/core/tests/coretests/BstatsTestApp/AndroidManifest.xml b/services/tests/powerstatstests/BstatsTestApp/AndroidManifest.xml index fcb1e71cc3f5..fcb1e71cc3f5 100644 --- a/core/tests/coretests/BstatsTestApp/AndroidManifest.xml +++ b/services/tests/powerstatstests/BstatsTestApp/AndroidManifest.xml diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/BaseCmdReceiver.java b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/BaseCmdReceiver.java index 2601f3571b98..2601f3571b98 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/BaseCmdReceiver.java +++ b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/BaseCmdReceiver.java diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/Common.java b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/Common.java index d192fbd66c89..c731e536d032 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/Common.java +++ b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/Common.java @@ -15,8 +15,6 @@ */ package com.android.coretests.apps.bstatstestapp; -import com.android.frameworks.coretests.aidl.ICmdCallback; - import android.content.Intent; import android.os.Bundle; import android.os.IBinder; @@ -24,6 +22,8 @@ import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; +import com.android.frameworks.coretests.aidl.ICmdCallback; + public class Common { private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver"; diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java index 892f60e8530f..892f60e8530f 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java +++ b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java index 5c551d54b4d5..5c551d54b4d5 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java +++ b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java index 0cd0643ee8c0..0cd0643ee8c0 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java +++ b/services/tests/powerstatstests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java diff --git a/services/tests/servicestests/src/com/android/server/powerstats/OWNERS b/services/tests/powerstatstests/OWNERS index 12f13ea63db0..9ed0e051738d 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/OWNERS +++ b/services/tests/powerstatstests/OWNERS @@ -1 +1,4 @@ +# Bug component: 987260 + +include /BATTERY_STATS_OWNERS include /services/core/java/com/android/server/powerstats/OWNERS diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING new file mode 100644 index 000000000000..e1eb1e4fc0db --- /dev/null +++ b/services/tests/powerstatstests/TEST_MAPPING @@ -0,0 +1,22 @@ +{ + "presubmit": [ + { + "name": "PowerStatsTests", + "options": [ + {"include-filter": "com.android.server.power.stats"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, + {"exclude-annotation": "androidx.test.filters.FlakyTest"}, + {"exclude-annotation": "org.junit.Ignore"} + ] + } + ], + "postsubmit": [ + { + "name": "PowerStatsTests", + "options": [ + {"include-filter": "com.android.server.power.stats"}, + {"exclude-annotation": "org.junit.Ignore"} + ] + } + ] +} diff --git a/services/tests/servicestests/res/xml/irq_device_map_1.xml b/services/tests/powerstatstests/res/xml/irq_device_map_1.xml index 1f1a77b437ab..1f1a77b437ab 100644 --- a/services/tests/servicestests/res/xml/irq_device_map_1.xml +++ b/services/tests/powerstatstests/res/xml/irq_device_map_1.xml diff --git a/services/tests/servicestests/res/xml/irq_device_map_2.xml b/services/tests/powerstatstests/res/xml/irq_device_map_2.xml index 508c98d871da..508c98d871da 100644 --- a/services/tests/servicestests/res/xml/irq_device_map_2.xml +++ b/services/tests/powerstatstests/res/xml/irq_device_map_2.xml diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/powerstatstests/res/xml/irq_device_map_3.xml index fd55428c48df..fd55428c48df 100644 --- a/services/tests/servicestests/res/xml/irq_device_map_3.xml +++ b/services/tests/powerstatstests/res/xml/irq_device_map_3.xml diff --git a/services/tests/servicestests/res/xml/power_profile_test_legacy_modem.xml b/services/tests/powerstatstests/res/xml/power_profile_test_legacy_modem.xml index 5335f9640738..5335f9640738 100644 --- a/services/tests/servicestests/res/xml/power_profile_test_legacy_modem.xml +++ b/services/tests/powerstatstests/res/xml/power_profile_test_legacy_modem.xml diff --git a/services/tests/servicestests/res/xml/power_profile_test_modem_calculator.xml b/services/tests/powerstatstests/res/xml/power_profile_test_modem_calculator.xml index f57bc0f70b5d..f57bc0f70b5d 100644 --- a/services/tests/servicestests/res/xml/power_profile_test_modem_calculator.xml +++ b/services/tests/powerstatstests/res/xml/power_profile_test_modem_calculator.xml diff --git a/services/tests/servicestests/res/xml/power_profile_test_modem_calculator_multiactive.xml b/services/tests/powerstatstests/res/xml/power_profile_test_modem_calculator_multiactive.xml index 4f5e674c60f5..4f5e674c60f5 100644 --- a/services/tests/servicestests/res/xml/power_profile_test_modem_calculator_multiactive.xml +++ b/services/tests/powerstatstests/res/xml/power_profile_test_modem_calculator_multiactive.xml diff --git a/services/tests/servicestests/res/xml/power_profile_test_modem_default.xml b/services/tests/powerstatstests/res/xml/power_profile_test_modem_default.xml index ab016fbb1f5d..ab016fbb1f5d 100644 --- a/services/tests/servicestests/res/xml/power_profile_test_modem_default.xml +++ b/services/tests/powerstatstests/res/xml/power_profile_test_modem_default.xml diff --git a/services/tests/servicestests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java index 319a280d10cc..319a280d10cc 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java index fb367b24168e..fb367b24168e 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java index 4ea0805ffaa7..4ea0805ffaa7 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java index 5a2d2e3d33fd..5a2d2e3d33fd 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java index 4d3fcb611f24..4d3fcb611f24 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java index 3f101a96d36c..3f101a96d36c 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCounterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCounterTest.java index 326639c54495..326639c54495 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCounterTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCounterTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java index 55ffa1a15a6b..55ffa1a15a6b 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsDualTimerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsDualTimerTest.java index d6acf8da7431..d6acf8da7431 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsDualTimerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsDualTimerTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsDurationTimerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsDurationTimerTest.java index 99520cd6fbad..99520cd6fbad 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsDurationTimerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsDurationTimerTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java index 4fde73bd8408..4fde73bd8408 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index 48ba765c3968..48ba765c3968 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java index f20f061230e2..5ebc6ca3f558 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java @@ -267,7 +267,7 @@ public class BatteryStatsImplTest { final long[][] delta3 = { {98545, 95768795, 76586, 548945, 57846}, {788876, 586, 578459, 8776984, 9578923}, - {3049509483598l, 4597834, 377654, 94589035, 7854}, + {3049509483598L, 4597834, 377654, 94589035, 7854}, {9493, 784, 99895, 8974893, 9879843} }; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsManagerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java index 7ae111711b6b..7ae111711b6b 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsManagerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java index 88b9522d4cb1..88b9522d4cb1 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java index a0fb631812f4..a0fb631812f4 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsResetTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsSamplingTimerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSamplingTimerTest.java index 784d673ed3f0..ee68bf8ae53c 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsSamplingTimerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSamplingTimerTest.java @@ -1,17 +1,17 @@ /* * 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 + * 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 + * 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. + * 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.power.stats; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsSensorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSensorTest.java index b8f0ce3456f8..9c70f376ca14 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsSensorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSensorTest.java @@ -1,17 +1,17 @@ /* * 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 + * 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 + * 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. + * 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.power.stats; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsServTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsServTest.java index 200eb1d0ad15..200eb1d0ad15 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsServTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsServTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsStopwatchTimerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsStopwatchTimerTest.java index fcae42a76f1b..18a366c178fd 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsStopwatchTimerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsStopwatchTimerTest.java @@ -1,17 +1,17 @@ /* * Copyright (C) 2017 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * 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 + * 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. + * 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.power.stats; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTimeBaseTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsTimeBaseTest.java index 5b47423f0d59..5b47423f0d59 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTimeBaseTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsTimeBaseTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTimerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsTimerTest.java index 14c5c5db5c1c..14c5c5db5c1c 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTimerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsTimerTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java index face849620d7..face849620d7 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java index 5df0acb65249..5df0acb65249 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index 534aa89e1699..534aa89e1699 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsStoreTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsStoreTest.java index b846e3a36656..b846e3a36656 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsStoreTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsStoreTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java index 266a22632a6d..266a22632a6d 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java index 4d4337c16757..4d4337c16757 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java index ccace40bb056..ccace40bb056 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java index 5fce32f0598a..5fce32f0598a 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java index 888bc623f669..888bc623f669 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java index 245faaf15cc8..245faaf15cc8 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java index 28f4799656b7..28f4799656b7 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java index 0f85fdc375fb..0f85fdc375fb 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java index 3f2a6d04c1e6..3f2a6d04c1e6 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java index 3d150af711f1..3d150af711f1 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/KernelWakelockReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java index c0f3c775ffe5..2edfc8e1e408 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/KernelWakelockReaderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java @@ -1,17 +1,17 @@ /* * 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 + * 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 + * 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. + * 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.power.stats; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/LongSamplingCounterArrayTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/LongSamplingCounterArrayTest.java index 2e962c364ed2..2e962c364ed2 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/LongSamplingCounterArrayTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/LongSamplingCounterArrayTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/LongSamplingCounterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/LongSamplingCounterTest.java index 0eac625051fc..0eac625051fc 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/LongSamplingCounterTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/LongSamplingCounterTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java index 2cce449c6c05..2cce449c6c05 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java index d3ec0d7e3f6e..888a1688c2a1 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java @@ -46,7 +46,7 @@ import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.frameworks.servicestests.R; +import com.android.frameworks.powerstatstests.R; import com.google.common.collect.Range; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java index 6d3f1f27b572..6d3f1f27b572 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockClock.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java index 5e57cc36797b..5e57cc36797b 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/MockClock.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/OWNERS b/services/tests/powerstatstests/src/com/android/server/power/stats/OWNERS index 9a7db1cb4fe4..9a7db1cb4fe4 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/OWNERS +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java index 372307985f04..372307985f04 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java index 474527040839..474527040839 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java index 80cbe0da402e..80cbe0da402e 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java index 4dae2d548057..4dae2d548057 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/UserPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/UserPowerCalculatorTest.java index f14745ef2daa..f14745ef2daa 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/UserPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/UserPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java index f578aa3b46be..f578aa3b46be 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java index f1961855f12f..f1961855f12f 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java index 113be8b19518..113be8b19518 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java index b81b776019f9..0dc836ba0400 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java @@ -34,7 +34,7 @@ import android.util.SparseIntArray; import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.android.frameworks.servicestests.R; +import com.android.frameworks.powerstatstests.R; import com.android.server.power.stats.wakeups.CpuWakeupStats.Wakeup; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java index 47a8f49e7025..9af288496bb9 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java @@ -23,7 +23,7 @@ import android.content.Context; import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.android.frameworks.servicestests.R; +import com.android.frameworks.powerstatstests.R; import com.android.internal.util.CollectionUtils; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java index 99bc25abc4a1..99bc25abc4a1 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java diff --git a/services/tests/servicestests/src/com/android/server/powerstats/IntervalRandomNoiseGeneratorTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/IntervalRandomNoiseGeneratorTest.java index 99621460f360..99621460f360 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/IntervalRandomNoiseGeneratorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/powerstats/IntervalRandomNoiseGeneratorTest.java diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 2ffe4aacda73..2ffe4aacda73 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 173c5f5dd40f..92ff7ab86247 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -112,7 +112,6 @@ android_test { }, data: [ - ":BstatsTestApp", ":JobTestApp", ":SimpleServiceTestApp1", ":SimpleServiceTestApp2", @@ -134,7 +133,6 @@ java_library { name: "servicestests-core-utils", srcs: [ "src/com/android/server/am/DeviceConfigSession.java", - "src/com/android/server/display/TestUtils.java", "src/com/android/server/pm/PackageSettingBuilder.java", "src/com/android/server/pm/parsing/TestPackageParser2.kt", ], diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index fbb0ca108ecd..b1d50399416a 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -28,7 +28,6 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> - <option name="test-file-name" value="BstatsTestApp.apk" /> <option name="test-file-name" value="FrameworksServicesTests.apk" /> <option name="test-file-name" value="JobTestApp.apk" /> <option name="test-file-name" value="SuspendTestApp.apk" /> diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java deleted file mode 100644 index 8b45145b160f..000000000000 --- a/services/tests/servicestests/src/com/android/server/display/TestUtils.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.display; - -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.input.InputSensorInfo; -import android.os.Parcel; -import android.os.SystemClock; -import android.view.DisplayAddress; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -public final class TestUtils { - - public static SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception { - final Constructor<SensorEvent> constructor = - SensorEvent.class.getDeclaredConstructor(int.class); - constructor.setAccessible(true); - final SensorEvent event = constructor.newInstance(1); - event.sensor = sensor; - event.values[0] = value; - event.timestamp = SystemClock.elapsedRealtimeNanos(); - return event; - } - - - public static void setSensorType(Sensor sensor, int type, String strType) throws Exception { - Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); - setter.setAccessible(true); - setter.invoke(sensor, type); - if (strType != null) { - Field f = sensor.getClass().getDeclaredField("mStringType"); - f.setAccessible(true); - f.set(sensor, strType); - } - } - - public static void setMaximumRange(Sensor sensor, float maximumRange) throws Exception { - Method setter = Sensor.class.getDeclaredMethod("setRange", Float.TYPE, Float.TYPE); - setter.setAccessible(true); - setter.invoke(sensor, maximumRange, 1); - } - - public static Sensor createSensor(int type, String strType) throws Exception { - Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); - constr.setAccessible(true); - Sensor sensor = constr.newInstance(); - setSensorType(sensor, type, strType); - return sensor; - } - - public static Sensor createSensor(int type, String strType, float maximumRange) - throws Exception { - Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); - constr.setAccessible(true); - Sensor sensor = constr.newInstance(); - setSensorType(sensor, type, strType); - setMaximumRange(sensor, maximumRange); - return sensor; - } - - public static Sensor createSensor(String type, String name) { - return new Sensor(new InputSensorInfo( - name, "vendor", 0, 0, 0, 1f, 1f, 1, 1, 1, 1, - type, "", 0, 0, 0)); - } - - /** - * Create a custom {@link DisplayAddress} to ensure we're not relying on any specific - * display-address implementation in our code. Intentionally uses default object (reference) - * equality rules. - */ - public static class TestDisplayAddress extends DisplayAddress { - @Override - public void writeToParcel(Parcel out, int flags) { } - } -} diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTests.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTests.java deleted file mode 100644 index 48290e5ca1ef..000000000000 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTests.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.power.stats; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ - AmbientDisplayPowerCalculatorTest.class, - AudioPowerCalculatorTest.class, - BatteryChargeCalculatorTest.class, - BatteryExternalStatsWorkerTest.class, - BatteryStatsCpuTimesTest.class, - BatteryStatsBackgroundStatsTest.class, - BatteryStatsBinderCallStatsTest.class, - BatteryStatsCounterTest.class, - BatteryStatsDualTimerTest.class, - BatteryStatsDurationTimerTest.class, - BatteryStatsHistoryIteratorTest.class, - BatteryStatsHistoryTest.class, - BatteryStatsImplTest.class, - BatteryStatsManagerTest.class, - BatteryStatsNoteTest.class, - BatteryStatsSamplingTimerTest.class, - BatteryStatsSensorTest.class, - BatteryStatsServTest.class, - BatteryStatsStopwatchTimerTest.class, - BatteryStatsTimeBaseTest.class, - BatteryStatsTimerTest.class, - BatteryUsageStatsProviderTest.class, - BatteryUsageStatsTest.class, - BatteryUsageStatsStoreTest.class, - BatteryStatsUserLifecycleTests.class, - BluetoothPowerCalculatorTest.class, - BstatsCpuTimesValidationTest.class, - CameraPowerCalculatorTest.class, - CpuPowerCalculatorTest.class, - CustomEnergyConsumerPowerCalculatorTest.class, - FlashlightPowerCalculatorTest.class, - GnssPowerCalculatorTest.class, - IdlePowerCalculatorTest.class, - KernelWakelockReaderTest.class, - LongSamplingCounterTest.class, - LongSamplingCounterArrayTest.class, - EnergyConsumerSnapshotTest.class, - MobileRadioPowerCalculatorTest.class, - ScreenPowerCalculatorTest.class, - SensorPowerCalculatorTest.class, - SystemServerCpuThreadReaderTest.class, - SystemServicePowerCalculatorTest.class, - UserPowerCalculatorTest.class, - VideoPowerCalculatorTest.class, - WakelockPowerCalculatorTest.class, - WifiPowerCalculatorTest.class, -}) -public class BatteryStatsTests { -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt index b34da72ea5d6..4adcc8bf5f0b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt @@ -25,10 +25,12 @@ import android.tools.common.flicker.config.FlickerConfig import android.tools.common.flicker.config.FlickerServiceConfig import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.server.wm.flicker.service.notification.scenarios.OpenAppFromLockscreenNotificationWithOverlayApp +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) +@Ignore("b/294418322: no notification launch animation exists when keyguard is occluded") class OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape : OpenAppFromLockscreenNotificationWithOverlayApp(NavBar.MODE_3BUTTON, Rotation.ROTATION_90) { @ExpectedScenarios(["APP_LAUNCH_FROM_NOTIFICATION"]) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt index b1638979cd0d..f7211e7287cf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt @@ -25,10 +25,12 @@ import android.tools.common.flicker.config.FlickerConfig import android.tools.common.flicker.config.FlickerServiceConfig import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.server.wm.flicker.service.notification.scenarios.OpenAppFromLockscreenNotificationWithOverlayApp +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) +@Ignore("b/294418322: no notification launch animation exists when keyguard is occluded") class OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait : OpenAppFromLockscreenNotificationWithOverlayApp(NavBar.MODE_3BUTTON, Rotation.ROTATION_0) { @ExpectedScenarios(["APP_LAUNCH_FROM_NOTIFICATION"]) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt index 19b533ed57af..1ade9560d90f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt @@ -25,10 +25,12 @@ import android.tools.common.flicker.config.FlickerConfig import android.tools.common.flicker.config.FlickerServiceConfig import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.server.wm.flicker.service.notification.scenarios.OpenAppFromLockscreenNotificationWithOverlayApp +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) +@Ignore("b/294418322: no notification launch animation exists when keyguard is occluded") class OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape : OpenAppFromLockscreenNotificationWithOverlayApp(NavBar.MODE_GESTURAL, Rotation.ROTATION_90) { @ExpectedScenarios(["APP_LAUNCH_FROM_NOTIFICATION"]) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt index c9ed4f42af06..ea26f0867c7f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt @@ -25,10 +25,12 @@ import android.tools.common.flicker.config.FlickerConfig import android.tools.common.flicker.config.FlickerServiceConfig import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.server.wm.flicker.service.notification.scenarios.OpenAppFromLockscreenNotificationWithOverlayApp +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) +@Ignore("b/294418322: no notification launch animation exists when keyguard is occluded") class OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait : OpenAppFromLockscreenNotificationWithOverlayApp(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) { @ExpectedScenarios(["APP_LAUNCH_FROM_NOTIFICATION"]) |