summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--Ravenwood.bp3
-rw-r--r--apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java7
-rw-r--r--apex/jobscheduler/service/Android.bp1
-rw-r--r--core/api/test-current.txt18
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java43
-rw-r--r--core/java/android/content/Intent.java6
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java3
-rw-r--r--core/java/android/hardware/biometrics/SensorProperties.java7
-rw-r--r--core/java/android/hardware/face/FaceManager.java27
-rw-r--r--core/java/android/hardware/face/FaceSensorProperties.java6
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl2
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java4
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java162
-rw-r--r--core/java/android/view/IWindow.aidl4
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java34
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java16
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java1
-rw-r--r--core/java/android/view/InsetsController.java72
-rw-r--r--core/java/android/view/InsetsResizeAnimationRunner.java2
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java2
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java48
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java185
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java12
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java121
-rw-r--r--core/java/com/android/internal/inputmethod/IImeTracker.aidl25
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethod.aidl8
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl8
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java32
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java31
-rw-r--r--core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java106
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl13
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerTest.java4
-rw-r--r--core/tests/coretests/src/android/os/WorkDurationUnitTest.java4
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java30
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java24
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java14
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java51
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java36
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java23
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt46
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java50
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt250
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt77
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt113
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt130
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt2
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java230
-rw-r--r--packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java25
-rw-r--r--ravenwood/Android.bp17
-rw-r--r--ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java54
-rw-r--r--ravenwood/test-authors.md18
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java2
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java3
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java36
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeTrackerService.java32
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java17
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java12
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java179
-rw-r--r--services/core/java/com/android/server/inputmethod/ZeroJankProxy.java7
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java1
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java25
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java15
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java18
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java28
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java8
-rw-r--r--services/core/java/com/android/server/wm/ClientLifecycleManager.java30
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java6
-rw-r--r--services/core/java/com/android/server/wm/InsetsControlTarget.java8
-rw-r--r--services/core/java/com/android/server/wm/SnapshotCache.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java54
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java70
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java27
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java21
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java7
162 files changed, 2407 insertions, 1908 deletions
diff --git a/Android.bp b/Android.bp
index 5ada10d19f5d..8d7ab983593d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -386,6 +386,7 @@ java_defaults {
// TODO(b/120066492): remove gps_debug and protolog.conf.json when the build
// system propagates "required" properly.
"gps_debug.conf",
+ "protolog.conf.json.gz",
"core.protolog.pb",
"framework-res",
// any install dependencies should go into framework-minus-apex-install-dependencies
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 4791640239b3..f43c37bf637d 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -205,8 +205,10 @@ android_ravenwood_libgroup {
// Provide runtime versions of utils linked in below
"junit",
"truth",
+ "flag-junit",
"ravenwood-framework",
"ravenwood-junit-impl",
+ "ravenwood-junit-impl-flag",
"mockito-ravenwood-prebuilt",
"inline-mockito-ravenwood-prebuilt",
],
@@ -220,6 +222,7 @@ android_ravenwood_libgroup {
libs: [
"junit",
"truth",
+ "flag-junit",
"ravenwood-framework",
"ravenwood-junit",
"mockito-ravenwood-prebuilt",
diff --git a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
index ba15796f47fe..fc3738c7134a 100644
--- a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
+++ b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
@@ -53,6 +53,7 @@ import com.android.adservices.service.adselection.AdSelectionConfigArgumentUtil;
import com.android.adservices.service.adselection.AdWithBidArgumentUtil;
import com.android.adservices.service.adselection.CustomAudienceBiddingSignalsArgumentUtil;
import com.android.adservices.service.adselection.CustomAudienceScoringSignalsArgumentUtil;
+import com.android.adservices.service.common.NoOpRetryStrategyImpl;
import com.android.adservices.service.js.IsolateSettings;
import com.android.adservices.service.js.JSScriptArgument;
import com.android.adservices.service.js.JSScriptArrayArgument;
@@ -411,7 +412,8 @@ public class JSScriptEnginePerfTests {
jsScript,
args,
functionName,
- IsolateSettings.forMaxHeapSizeEnforcementDisabled());
+ IsolateSettings.forMaxHeapSizeEnforcementDisabled(),
+ new NoOpRetryStrategyImpl());
result.addListener(resultLatch::countDown, sExecutorService);
return result;
}
@@ -430,7 +432,8 @@ public class JSScriptEnginePerfTests {
wasmScript,
args,
functionName,
- IsolateSettings.forMaxHeapSizeEnforcementDisabled());
+ IsolateSettings.forMaxHeapSizeEnforcementDisabled(),
+ new NoOpRetryStrategyImpl());
result.addListener(resultLatch::countDown, sExecutorService);
return result;
}
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 558629537253..0104ee14fec4 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -21,6 +21,7 @@ java_library {
libs: [
"app-compat-annotations",
+ "error_prone_annotations",
"framework",
"services.core",
"unsupportedappusage",
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index af40c3d658a7..a28dc497c508 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1547,6 +1547,10 @@ package android.hardware.biometrics {
method public boolean isAllowBackgroundAuthentication();
}
+ public abstract static class BiometricPrompt.AuthenticationCallback {
+ method @FlaggedApi("android.hardware.biometrics.face_background_authentication") public void onAuthenticationAcquired(int);
+ }
+
public static class BiometricPrompt.Builder {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean);
method @FlaggedApi("android.multiuser.enable_biometrics_to_unlock_private_space") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean, boolean);
@@ -1565,6 +1569,7 @@ package android.hardware.biometrics {
}
public class SensorProperties {
+ ctor @FlaggedApi("android.hardware.biometrics.face_background_authentication") public SensorProperties(int, int, @NonNull java.util.List<android.hardware.biometrics.SensorProperties.ComponentInfo>);
method @NonNull public java.util.List<android.hardware.biometrics.SensorProperties.ComponentInfo> getComponentInfo();
method public int getSensorId();
method public int getSensorStrength();
@@ -1709,6 +1714,18 @@ package android.hardware.display {
}
+package android.hardware.face {
+
+ @FlaggedApi("android.hardware.biometrics.face_background_authentication") public class FaceManager {
+ method @FlaggedApi("android.hardware.biometrics.face_background_authentication") @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
+ method @FlaggedApi("android.hardware.biometrics.face_background_authentication") @NonNull public java.util.List<android.hardware.face.FaceSensorProperties> getSensorProperties();
+ }
+
+ @FlaggedApi("android.hardware.biometrics.face_background_authentication") public class FaceSensorProperties extends android.hardware.biometrics.SensorProperties {
+ }
+
+}
+
package android.hardware.fingerprint {
@Deprecated public class FingerprintManager {
@@ -3944,6 +3961,7 @@ package android.view.inputmethod {
method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
+ method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest();
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 79696e047904..48081bb04863 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
import android.app.IApplicationThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -54,10 +55,12 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
@Nullable
private List<ClientTransactionItem> mTransactionItems;
- /** A list of individual callbacks to a client. */
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- @UnsupportedAppUsage
+ /** @deprecated use {@link #getTransactionItems} instead. */
@Nullable
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code #getTransactionItems()}")
+ @Deprecated
private List<ClientTransactionItem> mActivityCallbacks;
/**
@@ -126,42 +129,42 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
setActivityTokenIfNotSet(activityCallback);
}
- /**
- * Gets the list of callbacks.
- * @deprecated use {@link #getTransactionItems()} instead.
- */
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- @Nullable
+ /** @deprecated use {@link #getTransactionItems()} instead. */
@VisibleForTesting
- @UnsupportedAppUsage
+ @Nullable
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code #getTransactionItems()}")
@Deprecated
public List<ClientTransactionItem> getCallbacks() {
return mActivityCallbacks;
}
/**
- * @deprecated a transaction can contain {@link ClientTransactionItem} of different activities,
+ * A transaction can contain {@link ClientTransactionItem} of different activities,
* this must not be used. For any unsupported app usages, please be aware that this is set to
* the activity of the first item in {@link #getTransactionItems()}.
+ *
+ * @deprecated use {@link ClientTransactionItem#getActivityToken()} instead.
*/
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@VisibleForTesting
@Nullable
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code android.app.servertransaction"
+ + ".ClientTransactionItem#getActivityToken()}")
@Deprecated
public IBinder getActivityToken() {
return mActivityToken;
}
- /**
- * Gets the target state lifecycle request.
- * @deprecated use {@link #getTransactionItems()} instead.
- */
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+ /** @deprecated use {@link #getTransactionItems()} instead. */
@VisibleForTesting(visibility = PACKAGE)
- @UnsupportedAppUsage
- @Deprecated
@Nullable
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ trackingBug = 324203798,
+ publicAlternatives = "Use {@code #getTransactionItems()}")
+ @Deprecated
public ActivityLifecycleItem getLifecycleStateRequest() {
return mLifecycleStateRequest;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fd2af99b6a7e..42dd87a711f3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1064,7 +1064,11 @@ public class Intent implements Parcelable, Cloneable {
}
if (sender != null) {
- intent.putExtra(EXTRA_CHOOSER_RESULT_INTENT_SENDER, sender);
+ if (android.service.chooser.Flags.enableChooserResult()) {
+ intent.putExtra(EXTRA_CHOOSER_RESULT_INTENT_SENDER, sender);
+ } else {
+ intent.putExtra(EXTRA_CHOSEN_COMPONENT_INTENT_SENDER, sender);
+ }
}
// Migrate any clip data and flags from target.
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 0208fed6040f..d9d4305cc630 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -23,6 +23,7 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION;
import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
import static android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE;
@@ -1127,6 +1128,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* @hide
*/
@Override
+ @TestApi
+ @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
public void onAuthenticationAcquired(int acquireInfo) {}
/**
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
index 3b9cad49250f..16f71414c8c9 100644
--- a/core/java/android/hardware/biometrics/SensorProperties.java
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -16,6 +16,9 @@
package android.hardware.biometrics;
+import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
@@ -141,8 +144,10 @@ public class SensorProperties {
/**
* @hide
*/
+ @TestApi
+ @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
public SensorProperties(int sensorId, @Strength int sensorStrength,
- List<ComponentInfo> componentInfo) {
+ @NonNull List<ComponentInfo> componentInfo) {
mSensorId = sensorId;
mSensorStrength = sensorStrength;
mComponentInfo = componentInfo;
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 066c45f03ec4..1b0a485bdbda 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -18,6 +18,7 @@ package android.hardware.face;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
+import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
@@ -29,6 +30,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -36,6 +38,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricStateListener;
+import android.hardware.biometrics.BiometricTestSession;
import android.hardware.biometrics.CryptoObject;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.os.Binder;
@@ -69,6 +72,7 @@ import java.util.concurrent.Executor;
*/
@FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
@SystemApi
+@TestApi
@SystemService(Context.FACE_SERVICE)
public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
@@ -780,6 +784,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
* @hide
*/
@NonNull
+ @TestApi
+ @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
public List<FaceSensorProperties> getSensorProperties() {
final List<FaceSensorProperties> properties = new ArrayList<>();
final List<FaceSensorPropertiesInternal> internalProperties
@@ -1628,4 +1634,23 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
Slog.w(TAG, "Unknown enrollment acquired message: " + acquireInfo + ", " + vendorCode);
return null;
}
-}
+
+ /**
+ * Retrieves a test session for FaceManager.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(TEST_BIOMETRIC)
+ @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
+ public BiometricTestSession createTestSession(int sensorId) {
+ try {
+ return new BiometricTestSession(mContext, sensorId,
+ (context, sensorId1, callback) -> mService
+ .createTestSession(sensorId1, callback, context.getOpPackageName()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java
index f61312785919..a1ddb0e3495b 100644
--- a/core/java/android/hardware/face/FaceSensorProperties.java
+++ b/core/java/android/hardware/face/FaceSensorProperties.java
@@ -16,8 +16,12 @@
package android.hardware.face;
+import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
@@ -30,6 +34,8 @@ import java.util.List;
* Container for face sensor properties.
* @hide
*/
+@TestApi
+@FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
public class FaceSensorProperties extends SensorProperties {
/**
* @hide
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index b98c0cb41ac9..6515312e337c 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -39,7 +39,7 @@ import android.view.Surface;
interface IFaceService {
// Creates a test session with the specified sensorId
- @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ @EnforcePermission("TEST_BIOMETRIC")
ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName);
// Requests a proto dump of the specified sensor
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index f5b58b920efb..9dc8c5d9f2e9 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -453,7 +453,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ public void showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
@@ -462,7 +462,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
+ public void hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_HIDE_SOFT_INPUT,
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2c7ca27e6b07..4dbdd91d5fc7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -701,7 +701,13 @@ public class InputMethodService extends AbstractInputMethodService {
*/
private IBinder mCurHideInputToken;
- /** The token tracking the current IME request or {@code null} otherwise. */
+ /**
+ * The token tracking the current IME request.
+ *
+ * <p> This exists as a workaround to changing the signatures of public methods. It will get
+ * set to a {@code non-null} value before every call that uses it, stored locally inside the
+ * callee, and immediately after reset to {@code null} from the callee.
+ */
@Nullable
private ImeTracker.Token mCurStatsToken;
@@ -907,14 +913,13 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) {
+ IBinder hideInputToken, @NonNull ImeTracker.Token statsToken) {
mSystemCallingHideSoftInput = true;
mCurHideInputToken = hideInputToken;
mCurStatsToken = statsToken;
try {
hideSoftInput(flags, resultReceiver);
} finally {
- mCurStatsToken = null;
mCurHideInputToken = null;
mSystemCallingHideSoftInput = false;
}
@@ -926,23 +931,33 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- ImeTracker.forLogging().onProgress(
- mCurStatsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "hideSoftInput()");
+
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(false /* show */,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
+ // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
&& !mSystemCallingHideSoftInput) {
Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
+ " Use requestHideSelf(int) itself");
+ ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
+
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
mShowInputFlags = 0;
mShowInputRequested = false;
+ mCurStatsToken = statsToken;
hideWindow();
final boolean isVisible = isInputViewShown();
final boolean visibilityChanged = isVisible != wasVisible;
@@ -963,14 +978,13 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public void showSoftInputWithToken(@InputMethod.ShowFlags int flags,
ResultReceiver resultReceiver, IBinder showInputToken,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
mSystemCallingShowSoftInput = true;
mCurShowInputToken = showInputToken;
mCurStatsToken = statsToken;
try {
showSoftInput(flags, resultReceiver);
} finally {
- mCurStatsToken = null;
mCurShowInputToken = null;
mSystemCallingShowSoftInput = false;
}
@@ -982,16 +996,23 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
- ImeTracker.forLogging().onProgress(
- mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "showSoftInput()");
+
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(true /* show */,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
// TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
&& !mSystemCallingShowSoftInput) {
- Log.e(TAG," IME shouldn't call showSoftInput on itself."
+ Log.e(TAG, "IME shouldn't call showSoftInput on itself."
+ " Use requestShowSelf(int) itself");
+ ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
ImeTracing.getInstance().triggerServiceDump(
@@ -999,11 +1020,12 @@ public class InputMethodService extends AbstractInputMethodService {
null /* icProto */);
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
- ImeTracker.forLogging().onProgress(mCurStatsToken,
+ ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
- showWindow(true);
+ mCurStatsToken = statsToken;
+ showWindow(true /* showInput */);
} else {
- ImeTracker.forLogging().onFailed(mCurStatsToken,
+ ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
}
setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
@@ -1895,21 +1917,23 @@ public class InputMethodService extends AbstractInputMethodService {
if (showingInput) {
// If we were last showing the soft keyboard, try to do so again.
if (dispatchOnShowInputRequested(showFlags, true)) {
- showWindow(true);
+ showWindowWithToken(true /* showInput */,
+ SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
if (completions != null) {
mCurCompletions = completions;
onDisplayCompletions(completions);
}
} else {
- hideWindow();
+ hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
}
} else if (mCandidatesVisibility == View.VISIBLE) {
// If the candidates are currently visible, make sure the
// window is shown for them.
- showWindow(false);
+ showWindowWithToken(false /* showInput */,
+ SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
} else {
// Otherwise hide the window.
- hideWindow();
+ hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION);
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
@@ -2368,13 +2392,15 @@ public class InputMethodService extends AbstractInputMethodService {
// has not asked for the input view to be shown, then we need
// to update whether the window is shown.
if (shown) {
- showWindow(false);
+ showWindowWithToken(false /* showInput */,
+ SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY);
} else {
- hideWindow();
+ hideWindowWithToken(
+ SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY);
}
}
}
-
+
void updateCandidatesVisibility(boolean shown) {
int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
if (mCandidatesVisibility != vis) {
@@ -3009,6 +3035,19 @@ public class InputMethodService extends AbstractInputMethodService {
return result;
}
+ /**
+ * Utility function that creates an IME request tracking token before
+ * calling {@link #showWindow}.
+ *
+ * @param showInput whether the input window should be shown.
+ * @param reason the reason why the IME request was created.
+ */
+ private void showWindowWithToken(boolean showInput, @SoftInputShowHideReason int reason) {
+ mCurStatsToken = createStatsToken(true /* show */, reason,
+ ImeTracker.isFromUser(mRootView));
+ showWindow(showInput);
+ }
+
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
@@ -3018,11 +3057,20 @@ public class InputMethodService extends AbstractInputMethodService {
+ " mInputStarted=" + mInputStarted
+ " mShowInputFlags=" + mShowInputFlags);
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(true /* show */,
+ SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
if (mInShowWindow) {
Log.w(TAG, "Re-entrance in to showWindow");
+ ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW);
+
ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
null /* icProto */);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
@@ -3046,7 +3094,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
mWindow.show();
mDecorViewWasVisible = true;
- applyVisibilityInInsetsConsumerIfNecessary(true);
+ applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */, statsToken);
cancelImeSurfaceRemoval();
mInShowWindow = false;
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3137,13 +3185,15 @@ public class InputMethodService extends AbstractInputMethodService {
* Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}.
*
* @param setVisible {@code true} to make it visible, false to hide it.
+ * @param statsToken the token tracking the current IME request.
*/
- private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
+ private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible,
+ @NonNull ImeTracker.Token statsToken) {
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
null /* icProto */);
mPrivOps.applyImeVisibilityAsync(setVisible
- ? mCurShowInputToken : mCurHideInputToken, setVisible, mCurStatsToken);
+ ? mCurShowInputToken : mCurHideInputToken, setVisible, statsToken);
}
private void finishViews(boolean finishingInput) {
@@ -3159,12 +3209,35 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesViewStarted = false;
}
+ /**
+ * Utility function that creates an IME request tracking token before
+ * calling {@link #hideWindow}.
+ *
+ * @param reason the reason why the IME request was created.
+ */
+ private void hideWindowWithToken(@SoftInputShowHideReason int reason) {
+ // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it
+ // to work with onClickListeners
+ final boolean isFromUser = ImeTracker.isFromUser(mRootView)
+ || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
+ mCurStatsToken = createStatsToken(false /* show */, reason, isFromUser);
+ hideWindow();
+ }
+
public void hideWindow() {
if (DEBUG) Log.v(TAG, "CALL: hideWindow");
+
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsToken(false /* show */,
+ SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT,
+ ImeTracker.isFromUser(mRootView));
+ mCurStatsToken = null;
+
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
setImeWindowStatus(0, mBackDisposition);
- applyVisibilityInInsetsConsumerIfNecessary(false);
+ applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */, statsToken);
mWindowVisible = false;
finishViews(false /* finishingInput */);
if (mDecorViewVisible) {
@@ -3440,9 +3513,14 @@ public class InputMethodService extends AbstractInputMethodService {
private void requestHideSelf(@InputMethodManager.HideFlags int flags,
@SoftInputShowHideReason int reason) {
+ // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it
+ // to work with onClickListeners
+ final boolean isFromUser = ImeTracker.isFromUser(mRootView)
+ || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
+ final var statsToken = createStatsToken(false /* show */, reason, isFromUser);
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
null /* icProto */);
- mPrivOps.hideMySoftInput(flags, reason);
+ mPrivOps.hideMySoftInput(statsToken, flags, reason);
}
/**
@@ -3450,9 +3528,16 @@ public class InputMethodService extends AbstractInputMethodService {
* interact with it.
*/
public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) {
+ requestShowSelf(flags, SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME);
+ }
+
+ private void requestShowSelf(@InputMethodManager.ShowFlags int flags,
+ @SoftInputShowHideReason int reason) {
+ final var statsToken = createStatsToken(true /* show */, reason,
+ ImeTracker.isFromUser(mRootView));
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
null /* icProto */);
- mPrivOps.showMySoftInput(flags);
+ mPrivOps.showMySoftInput(statsToken, flags, reason);
}
private boolean handleBack(boolean doIt) {
@@ -3472,7 +3557,7 @@ public class InputMethodService extends AbstractInputMethodService {
// If we have the window visible for some other reason --
// most likely to show candidates -- then just get rid
// of it. This really shouldn't happen, but just in case...
- if (doIt) hideWindow();
+ if (doIt) hideWindowWithToken(SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY);
}
return true;
}
@@ -3627,10 +3712,11 @@ public class InputMethodService extends AbstractInputMethodService {
@InputMethodManager.HideFlags int hideFlags) {
if (DEBUG) Log.v(TAG, "toggleSoftInput()");
if (isInputViewShown()) {
- requestHideSelf(
- hideFlags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
+ requestHideSelf(hideFlags,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
} else {
- requestShowSelf(showFlags);
+ requestShowSelf(showFlags,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
}
}
@@ -4272,6 +4358,20 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * Creates an IME request tracking token.
+ *
+ * @param show whether this is a show or a hide request.
+ * @param reason the reason why the IME request was created.
+ * @param isFromUser whether this request was created directly from user interaction.
+ */
+ @NonNull
+ private ImeTracker.Token createStatsToken(boolean show, @SoftInputShowHideReason int reason,
+ boolean isFromUser) {
+ return ImeTracker.forLogging().onStart(show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_IME, reason, isFromUser);
+ }
+
+ /**
* Performs a dump of the InputMethodService's internal state. Override
* to add your own information to the dump.
*/
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 11180aef4479..5ee526e0343d 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -73,7 +73,7 @@ oneway interface IWindow {
*
* @param types internal insets types (WindowInsets.Type.InsetsType) to show
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
void showInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
@@ -82,7 +82,7 @@ oneway interface IWindow {
*
* @param types internal insets types (WindowInsets.Type.InsetsType) to hide
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
void hideInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index de809c8489fd..821e13d85370 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -21,9 +21,9 @@ import static android.view.ImeInsetsSourceConsumerProto.HAS_PENDING_REQUEST;
import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
-import android.os.Process;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
@@ -70,7 +70,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
if (!isShowRequested()) {
mIsRequestedVisibleAwaitingControl = false;
if (!running && !mHasPendingRequest) {
- notifyHidden(null /* statsToken */);
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED,
+ mController.getHost().isHandlingPointerEvent() /* fromUser */);
+ notifyHidden(statsToken);
removeSurface();
}
}
@@ -144,9 +148,17 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
if (!fromIme) {
+ // Create a new token to track the hide request when we have control,
+ // as we use the passed in token for the insets animation already.
+ final var notifyStatsToken = getControl() != null
+ ? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
+ mController.getHost().isHandlingPointerEvent() /* fromUser */)
+ : statsToken;
// The insets might be controlled by a remote target. Let the server know we are
// requested to hide.
- notifyHidden(statsToken);
+ notifyHidden(notifyStatsToken);
}
if (mAnimationState == ANIMATION_STATE_SHOW) {
mHasPendingRequest = true;
@@ -157,21 +169,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
* Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
* IME insets are hidden.
*
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
- private void notifyHidden(@Nullable ImeTracker.Token statsToken) {
- // Create a new stats token to track the hide request when:
- // - we do not already have one, or
- // - we do already have one, but we have control and use the passed in token
- // for the insets animation already.
- if (statsToken == null || getControl() != null) {
- statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
- Process.myUid(),
- ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
- mController.getHost().isHandlingPointerEvent() /* fromUser */);
- }
-
+ private void notifyHidden(@NonNull ImeTracker.Token statsToken) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 7f1e037e92d4..85c779bc8c79 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -43,7 +43,6 @@ import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
-import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -165,9 +164,9 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
mStatsToken = statsToken;
if (DEBUG_IME_VISIBILITY && (types & ime()) != 0) {
EventLog.writeEvent(IMF_IME_ANIM_START,
- mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType,
- mCurrentAlpha, "Current:" + mCurrentInsets, "Shown:" + mShownInsets,
- "Hidden:" + mHiddenInsets);
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mAnimationType, mCurrentAlpha, "Current:" + mCurrentInsets,
+ "Shown:" + mShownInsets, "Hidden:" + mHiddenInsets);
}
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets));
@@ -245,6 +244,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
}
@Override
+ @Nullable
public ImeTracker.Token getStatsToken() {
return mStatsToken;
}
@@ -330,8 +330,8 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
mListener.onFinished(this);
if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) {
EventLog.writeEvent(IMF_IME_ANIM_FINISH,
- mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType,
- mCurrentAlpha, shown ? 1 : 0, Objects.toString(insets));
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mAnimationType, mCurrentAlpha, shown ? 1 : 0, Objects.toString(insets));
}
}
@@ -355,8 +355,8 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
if (DEBUG) Log.d(TAG, "notify Control request cancelled for types: " + mTypes);
if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) {
EventLog.writeEvent(IMF_IME_ANIM_CANCEL,
- mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType,
- Objects.toString(mPendingInsets));
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mAnimationType, Objects.toString(mPendingInsets));
}
releaseLeashes();
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 079991a81e77..92e20e09d8c4 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -137,6 +137,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
}
@Override
+ @Nullable
public ImeTracker.Token getStatsToken() {
return mControl.getStatsToken();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 1803a6ed237f..6cc4b20dcde9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -28,7 +28,6 @@ import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.ime;
-import static android.view.inputmethod.ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL;
import android.animation.AnimationHandler;
import android.animation.Animator;
@@ -47,7 +46,6 @@ import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Process;
import android.os.Trace;
import android.text.TextUtils;
import android.util.IntArray;
@@ -659,6 +657,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private final Runnable mAnimCallback;
/** Pending control request that is waiting on IME to be ready to be shown */
+ @Nullable
private PendingControlRequest mPendingImeControlRequest;
private int mWindowType;
@@ -1043,12 +1042,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
hideTypes[0] &= ~animatingTypes;
if (showTypes[0] != 0) {
- applyAnimation(showTypes[0], true /* show */, false /* fromIme */,
- null /* statsToken */);
+ final var statsToken = (showTypes[0] & ime()) == 0 ? null
+ : ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken);
}
if (hideTypes[0] != 0) {
- applyAnimation(hideTypes[0], false /* show */, false /* fromIme */,
- null /* statsToken */);
+ final var statsToken = (hideTypes[0] & ime()) == 0 ? null
+ : ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken);
}
if (mControllableTypes != controllableTypes) {
@@ -1064,15 +1069,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void show(@InsetsType int types) {
- ImeTracker.Token statsToken = null;
- if ((types & ime()) != 0) {
- statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API,
- mHost.isHandlingPointerEvent() /* fromUser */);
- }
-
- show(types, false /* fromIme */, statsToken);
+ show(types, false /* fromIme */, null /* statsToken */);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -1080,6 +1077,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Nullable ImeTracker.Token statsToken) {
if ((types & ime()) != 0) {
Log.d(TAG, "show(ime(), fromIme=" + fromIme + ")");
+
+ if (statsToken == null) {
+ statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ }
}
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("InsetsController#show",
@@ -1148,9 +1152,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
/**
- * Handle the {@link #mPendingImeControlRequest} when
- * - The IME insets is ready to show.
- * - The IME insets has being requested invisible.
+ * Handle the {@link #mPendingImeControlRequest} when:
+ * <ul>
+ * <li> The IME insets is ready to show.
+ * <li> The IME insets has being requested invisible.
+ * </ul>
*/
private void handlePendingControlRequest(@Nullable ImeTracker.Token statsToken) {
PendingControlRequest pendingRequest = mPendingImeControlRequest;
@@ -1170,20 +1176,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void hide(@InsetsType int types) {
- ImeTracker.Token statsToken = null;
- if ((types & ime()) != 0) {
- statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
- mHost.isHandlingPointerEvent() /* fromUser */);
- }
-
- hide(types, false /* fromIme */, statsToken);
+ hide(types, false /* fromIme */, null /* statsToken */);
}
@VisibleForTesting
public void hide(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
+ if ((types & ime()) != 0) {
+ Log.d(TAG, "hide(ime(), fromIme=" + fromIme + ")");
+
+ if (statsToken == null) {
+ statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
+ mHost.isHandlingPointerEvent() /* fromUser */);
+ }
+ }
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("InsetsController#hide",
mHost.getInputMethodManager(), null /* icProto */);
@@ -1307,10 +1315,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (monitoredAnimation && (types & Type.ime()) != 0) {
if (animationType == ANIMATION_TYPE_SHOW) {
ImeTracker.forLatency().onShowCancelled(statsToken,
- PHASE_CLIENT_ANIMATION_CANCEL, ActivityThread::currentApplication);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL,
+ ActivityThread::currentApplication);
} else {
ImeTracker.forLatency().onHideCancelled(statsToken,
- PHASE_CLIENT_ANIMATION_CANCEL, ActivityThread::currentApplication);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL,
+ ActivityThread::currentApplication);
}
ImeTracker.forLogging().onCancelled(statsToken,
ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION);
@@ -1602,12 +1612,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
if (invokeCallback) {
ImeTracker.forLogging().onCancelled(control.getStatsToken(),
- PHASE_CLIENT_ANIMATION_CANCEL);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL);
control.cancel();
} else {
// Succeeds if invokeCallback is false (i.e. when called from notifyFinished).
ImeTracker.forLogging().onProgress(control.getStatsToken(),
- PHASE_CLIENT_ANIMATION_CANCEL);
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL);
}
if (DEBUG) {
Log.d(TAG, TextUtils.formatSimple(
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index bffaeea6a731..ebdddd537ae3 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -29,6 +29,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.SparseArray;
@@ -92,6 +93,7 @@ public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner
}
@Override
+ @Nullable
public ImeTracker.Token getStatsToken() {
// Return null as resizing the IME view is not explicitly tracked.
return null;
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 0ce61bb17774..fdb2a6ee1791 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -314,7 +314,7 @@ public class InsetsSourceConsumer {
* @param fromController {@code true} if request is coming from controller.
* (e.g. in IME case, controller is
* {@link android.inputmethodservice.InputMethodService}).
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*
* @implNote The {@code statsToken} is ignored here, and only handled in
* {@link ImeInsetsSourceConsumer} for IME animations only.
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 491b0e349cde..cedf8d04ed99 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -25,7 +25,6 @@ import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -298,7 +297,7 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
@@ -315,7 +314,7 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
@Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {
@@ -331,6 +330,20 @@ final class IInputMethodManagerGlobalInvoker {
// TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
@AnyThread
+ @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
+ static void hideSoftInputFromServerForTest() {
+ final IInputMethodManager service = getService();
+ if (service == null) {
+ return;
+ }
+ try {
+ service.hideSoftInputFromServerForTest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @AnyThread
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
static InputBindResult startInputOrWindowGainedFocus(@StartInputReason int startInputReason,
@@ -654,35 +667,18 @@ final class IInputMethodManagerGlobalInvoker {
}
}
- /** @see com.android.server.inputmethod.ImeTrackerService#onRequestShow */
- @AnyThread
- @NonNull
- static ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
- final IImeTracker service = getImeTrackerService();
- if (service == null) {
- // Create token with "fake" binder if the service was not found.
- return new ImeTracker.Token(new Binder(), tag);
- }
- try {
- return service.onRequestShow(tag, uid, origin, reason, fromUser);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** @see com.android.server.inputmethod.ImeTrackerService#onRequestHide */
+ /** @see com.android.server.inputmethod.ImeTrackerService#onStart */
@AnyThread
@NonNull
- static ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
+ static ImeTracker.Token onStart(@NonNull String tag, int uid, @ImeTracker.Type int type,
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
- final IImeTracker service = getImeTrackerService();
+ final var service = getImeTrackerService();
if (service == null) {
- // Create token with "fake" binder if the service was not found.
- return new ImeTracker.Token(new Binder(), tag);
+ // Create token with "empty" binder if the service was not found.
+ return ImeTracker.Token.empty(tag);
}
try {
- return service.onRequestHide(tag, uid, origin, reason, fromUser);
+ return service.onStart(tag, uid, type, origin, reason, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index b1fdaa97ffe0..7f796611d217 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -28,17 +28,20 @@ import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_SHOWN;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityThread;
import android.content.Context;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.os.SystemProperties;
import android.util.Log;
import android.view.InsetsController.AnimationType;
import android.view.SurfaceControl;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.jank.InteractionJankMonitor;
@@ -108,34 +111,32 @@ public interface ImeTracker {
/**
* The origin of the IME request
*
- * The name follows the format {@code PHASE_x_...} where {@code x} denotes
- * where the origin is (i.e. {@code PHASE_SERVER_...} occurs in the server).
+ * <p> The name follows the format {@code ORIGIN_x_...} where {@code x} denotes
+ * where the origin is (i.e. {@code ORIGIN_SERVER} occurs in the server).
*/
@IntDef(prefix = { "ORIGIN_" }, value = {
- ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- ORIGIN_SERVER_START_INPUT,
- ORIGIN_SERVER_HIDE_INPUT
+ ORIGIN_CLIENT,
+ ORIGIN_SERVER,
+ ORIGIN_IME
})
@Retention(RetentionPolicy.SOURCE)
@interface Origin {}
- /** The IME show request originated in the client. */
- int ORIGIN_CLIENT_SHOW_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_SHOW_SOFT_INPUT;
+ /** The IME request originated in the client. */
+ int ORIGIN_CLIENT = ImeProtoEnums.ORIGIN_CLIENT;
- /** The IME hide request originated in the client. */
- int ORIGIN_CLIENT_HIDE_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_HIDE_SOFT_INPUT;
+ /** The IME request originated in the server. */
+ int ORIGIN_SERVER = ImeProtoEnums.ORIGIN_SERVER;
- /** The IME show request originated in the server. */
- int ORIGIN_SERVER_START_INPUT = ImeProtoEnums.ORIGIN_SERVER_START_INPUT;
-
- /** The IME hide request originated in the server. */
- int ORIGIN_SERVER_HIDE_INPUT = ImeProtoEnums.ORIGIN_SERVER_HIDE_INPUT;
+ /** The IME request originated in the IME. */
+ int ORIGIN_IME = ImeProtoEnums.ORIGIN_IME;
+ /** The IME request originated in the WindowManager Shell. */
+ int ORIGIN_WM_SHELL = ImeProtoEnums.ORIGIN_WM_SHELL;
/**
* The current phase of the IME request.
*
- * The name follows the format {@code PHASE_x_...} where {@code x} denotes
+ * <p> The name follows the format {@code PHASE_x_...} where {@code x} denotes
* where the phase is (i.e. {@code PHASE_SERVER_...} occurs in the server).
*/
@IntDef(prefix = { "PHASE_" }, value = {
@@ -155,7 +156,6 @@ public interface ImeTracker {
PHASE_IME_SHOW_SOFT_INPUT,
PHASE_IME_HIDE_SOFT_INPUT,
PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE,
- PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER,
PHASE_SERVER_APPLY_IME_VISIBILITY,
PHASE_WM_SHOW_IME_RUNNER,
PHASE_WM_SHOW_IME_READY,
@@ -182,6 +182,11 @@ public interface ImeTracker {
PHASE_CLIENT_ANIMATION_FINISHED_SHOW,
PHASE_CLIENT_ANIMATION_FINISHED_HIDE,
PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT,
+ PHASE_CLIENT_ANIMATION_FINISHED_HIDE,
+ PHASE_IME_SHOW_WINDOW,
+ PHASE_IME_HIDE_WINDOW,
+ PHASE_IME_PRIVILEGED_OPERATIONS,
+ PHASE_SERVER_CURRENT_ACTIVE_IME,
})
@Retention(RetentionPolicy.SOURCE)
@interface Phase {}
@@ -224,19 +229,15 @@ public interface ImeTracker {
/** Dispatched from the IME wrapper to the IME. */
int PHASE_IME_WRAPPER_DISPATCH = ImeProtoEnums.PHASE_IME_WRAPPER_DISPATCH;
- /** Reached the IME' showSoftInput method. */
+ /** Reached the IME's showSoftInput method. */
int PHASE_IME_SHOW_SOFT_INPUT = ImeProtoEnums.PHASE_IME_SHOW_SOFT_INPUT;
- /** Reached the IME' hideSoftInput method. */
+ /** Reached the IME's hideSoftInput method. */
int PHASE_IME_HIDE_SOFT_INPUT = ImeProtoEnums.PHASE_IME_HIDE_SOFT_INPUT;
/** The server decided the IME should be shown. */
int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = ImeProtoEnums.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE;
- /** Requested applying the IME visibility in the insets source consumer. */
- int PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER =
- ImeProtoEnums.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER;
-
/** Applied the IME visibility. */
int PHASE_SERVER_APPLY_IME_VISIBILITY = ImeProtoEnums.PHASE_SERVER_APPLY_IME_VISIBILITY;
@@ -323,37 +324,49 @@ public interface ImeTracker {
int PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT =
ImeProtoEnums.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT;
+ /** Reached the IME's showWindow method. */
+ int PHASE_IME_SHOW_WINDOW = ImeProtoEnums.PHASE_IME_SHOW_WINDOW;
+
+ /** Reached the IME's hideWindow method. */
+ int PHASE_IME_HIDE_WINDOW = ImeProtoEnums.PHASE_IME_HIDE_WINDOW;
+
+ /** Reached the InputMethodPrivilegedOperations handler. */
+ int PHASE_IME_PRIVILEGED_OPERATIONS = ImeProtoEnums.PHASE_IME_PRIVILEGED_OPERATIONS;
+
+ /** Checked that the calling IME is the currently active IME. */
+ int PHASE_SERVER_CURRENT_ACTIVE_IME = ImeProtoEnums.PHASE_SERVER_CURRENT_ACTIVE_IME;
+
/**
- * Creates an IME show request tracking token.
+ * Called when an IME request is started.
*
- * @param component the name of the component that created the IME request, or {@code null}
- * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME show request.
- * @param reason the reason why the IME show request was created.
+ * @param component the name of the component that started the request.
+ * @param uid the uid of the client that started the request.
+ * @param type the type of the request.
+ * @param origin the origin of the request.
+ * @param reason the reason for starting the request.
* @param fromUser whether this request was created directly from user interaction.
*
- * @return An IME tracking token.
+ * @return An IME request tracking token.
*/
@NonNull
- Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
+ Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
@SoftInputShowHideReason int reason, boolean fromUser);
/**
- * Creates an IME hide request tracking token.
+ * Called when an IME request is started for the current process.
*
- * @param component the name of the component that created the IME request, or {@code null}
- * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME hide request.
- * @param reason the reason why the IME hide request was created.
+ * @param type the type of the request.
+ * @param origin the origin of the request.
+ * @param reason the reason for starting the request.
* @param fromUser whether this request was created directly from user interaction.
*
- * @return An IME tracking token.
+ * @return An IME request tracking token.
*/
@NonNull
- Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason, boolean fromUser);
+ default Token onStart(@Type int type, @Origin int origin, @SoftInputShowHideReason int reason,
+ boolean fromUser) {
+ return onStart(Process.myProcessName(), Process.myUid(), type, origin, reason, fromUser);
+ }
/**
* Called when an IME request progresses to a further phase.
@@ -390,14 +403,14 @@ public interface ImeTracker {
/**
* Called when the IME show request is successful.
*
- * @param token the token tracking the current IME show request or {@code null} otherwise.
+ * @param token the token tracking the current IME request or {@code null} otherwise.
*/
void onShown(@Nullable Token token);
/**
* Called when the IME hide request is successful.
*
- * @param token the token tracking the current IME hide request or {@code null} otherwise.
+ * @param token the token tracking the current IME request or {@code null} otherwise.
*/
void onHidden(@Nullable Token token);
@@ -479,33 +492,17 @@ public interface ImeTracker {
@NonNull
@Override
- public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
- @SoftInputShowHideReason int reason, boolean fromUser) {
- final var tag = getTag(component);
- final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin,
- reason, fromUser);
-
- Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
- + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
- + " fromUser " + fromUser,
- mLogStackTrace ? new Throwable() : null);
-
- return token;
- }
-
- @NonNull
- @Override
- public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
+ public Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
@SoftInputShowHideReason int reason, boolean fromUser) {
- final var tag = getTag(component);
- final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin,
- reason, fromUser);
+ final var tag = Token.createTag(component);
+ final var token = IInputMethodManagerGlobalInvoker.onStart(tag, uid, type,
+ origin, reason, fromUser);
- Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
+ Log.i(TAG, token.mTag + ": onRequest" + (type == TYPE_SHOW ? "Show" : "Hide")
+ + " at " + Debug.originToString(origin)
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
+ " fromUser " + fromUser,
mLogStackTrace ? new Throwable() : null);
-
return token;
}
@@ -556,20 +553,6 @@ public interface ImeTracker {
Log.i(TAG, token.mTag + ": onHidden");
}
-
- /**
- * Returns a logging tag using the given component name.
- *
- * @param component the name of the component that created the IME request, or {@code null}
- * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
- */
- @NonNull
- private String getTag(@Nullable String component) {
- if (component == null) {
- component = ActivityThread.currentProcessName();
- }
- return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
- }
};
/** The singleton IME tracker instance for instrumenting jank metrics. */
@@ -581,6 +564,10 @@ public interface ImeTracker {
/** A token that tracks the progress of an IME request. */
final class Token implements Parcelable {
+ /** Empty binder, lazily initialized, used for empty token instantiation. */
+ @Nullable
+ private static IBinder sEmptyBinder;
+
/** The binder used to identify this token. */
@NonNull
private final IBinder mBinder;
@@ -599,16 +586,56 @@ public interface ImeTracker {
mTag = in.readString8();
}
+ /** Returns the binder used to identify this token. */
@NonNull
public IBinder getBinder() {
return mBinder;
}
+ /** Returns the logging tag of this token. */
@NonNull
public String getTag() {
return mTag;
}
+ /**
+ * Creates a logging tag.
+ *
+ * @param component the name of the component that created the IME request.
+ */
+ @NonNull
+ private static String createTag(@NonNull String component) {
+ return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
+ }
+
+ /** Returns a new token with an empty binder. */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public static Token empty() {
+ final var tag = createTag(Process.myProcessName());
+ return empty(tag);
+ }
+
+ /** Returns a new token with an empty binder and the given logging tag. */
+ @NonNull
+ static Token empty(@NonNull String tag) {
+ return new Token(getEmptyBinder(), tag);
+ }
+
+ /** Returns the empty binder instance for empty token creation, lazily initializing it. */
+ @NonNull
+ private static IBinder getEmptyBinder() {
+ if (sEmptyBinder == null) {
+ sEmptyBinder = new Binder();
+ }
+ return sEmptyBinder;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "(tag: " + mTag + ")";
+ }
+
/** For Parcelable, no special marshalled objects. */
@Override
public int describeContents() {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 33f34c5697c4..88607fc80f69 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -281,7 +281,7 @@ public interface InputMethod {
})
@Retention(RetentionPolicy.SOURCE)
@interface ShowFlags {}
-
+
/**
* Flag for {@link #showSoftInput}: this show has been explicitly
* requested by the user. If not set, the system has decided it may be
@@ -314,18 +314,18 @@ public interface InputMethod {
* @param showInputToken an opaque {@link android.os.Binder} token to identify which API call
* of {@link InputMethodManager#showSoftInput(View, int)} is associated with
* this callback.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
* @hide
*/
@MainThread
public default void showSoftInputWithToken(@ShowFlags int flags, ResultReceiver resultReceiver,
- IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
+ IBinder showInputToken, @NonNull ImeTracker.Token statsToken) {
showSoftInput(flags, resultReceiver);
}
/**
* Request that any soft input part of the input method be shown to the user.
- *
+ *
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
* The result code should be
@@ -352,12 +352,12 @@ public interface InputMethod {
* @param hideInputToken an opaque {@link android.os.Binder} token to identify which API call
* of {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}} is associated
* with this callback.
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
* @hide
*/
@MainThread
public default void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) {
+ IBinder hideInputToken, @NonNull ImeTracker.Token statsToken) {
hideSoftInput(flags, resultReceiver);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 68940d699076..3be76cc8a60f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2263,21 +2263,22 @@ public final class InputMethodManager {
* {@link #RESULT_HIDDEN}.
*/
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
- return showSoftInput(view, null /* statsToken */, flags, resultReceiver,
- SoftInputShowHideReason.SHOW_SOFT_INPUT);
+ return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
- private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken,
- @ShowFlags int flags, ResultReceiver resultReceiver,
+ private boolean showSoftInput(View view, @ShowFlags int flags,
+ @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ // TODO(b/303041796): handle tracking physical keyboard and DPAD as user interactions
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(view));
+ return showSoftInput(view, statsToken, flags, resultReceiver, reason);
+ }
+
+ private boolean showSoftInput(View view, @NonNull ImeTracker.Token statsToken,
+ @ShowFlags int flags, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
- if (statsToken == null) {
- // TODO(b/303041796): handle tracking physical keyboard and DPAD as user interactions
- statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason,
- ImeTracker.isFromUser(view));
- }
- ImeTracker.forLatency().onRequestShow(statsToken, ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- reason, ActivityThread::currentApplication);
+ ImeTracker.forLatency().onRequestShow(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this,
null /* icProto */);
// Re-dispatch if there is a context mismatch.
@@ -2290,9 +2291,8 @@ public final class InputMethodManager {
synchronized (mH) {
if (!hasServedByInputMethodLocked(view)) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
- ImeTracker.forLatency().onShowFailed(
- statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED,
- ActivityThread::currentApplication);
+ ImeTracker.forLatency().onShowFailed(statsToken,
+ ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication);
Log.w(TAG, "Ignoring showSoftInput() as view=" + view + " is not served.");
return false;
}
@@ -2327,9 +2327,9 @@ public final class InputMethodManager {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499)
public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) {
synchronized (mH) {
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
- SoftInputShowHideReason.SHOW_SOFT_INPUT, false /* fromUser */);
+ final int reason = SoftInputShowHideReason.SHOW_SOFT_INPUT;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be"
+ " removed soon. If you are using androidx.appcompat.widget.SearchView,"
@@ -2353,7 +2353,7 @@ public final class InputMethodManager {
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- SoftInputShowHideReason.SHOW_SOFT_INPUT);
+ reason);
}
}
@@ -2429,11 +2429,10 @@ public final class InputMethodManager {
initialServedView = getServedViewLocked();
}
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ImeTracker.isFromUser(initialServedView));
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ActivityThread::currentApplication);
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(initialServedView));
+ ImeTracker.forLatency().onRequestHide(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow",
this, null /* icProto */);
checkFocus();
@@ -2472,20 +2471,18 @@ public final class InputMethodManager {
}
}
- final var reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW;
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ImeTracker.isFromUser(view));
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- reason, ActivityThread::currentApplication);
+ final int reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(view));
+ ImeTracker.forLatency().onRequestHide(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromView",
this, null /* icProto */);
synchronized (mH) {
if (!hasServedByInputMethodLocked(view)) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
- ImeTracker.forLatency().onShowFailed(
- statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED,
- ActivityThread::currentApplication);
+ ImeTracker.forLatency().onShowFailed(statsToken,
+ ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication);
Log.w(TAG, "Ignoring hideSoftInputFromView() as view=" + view + " is not served.");
return false;
}
@@ -2498,6 +2495,19 @@ public final class InputMethodManager {
}
/**
+ * A test API for CTS to request hiding the current soft input window, with the request origin
+ * on the server side.
+ *
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @TestApi
+ @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() {
+ IInputMethodManagerGlobalInvoker.hideSoftInputFromServerForTest();
+ }
+
+ /**
* Start stylus handwriting session.
*
* If supported by the current input method, a stylus handwriting session is started on the
@@ -2973,10 +2983,11 @@ public final class InputMethodManager {
if (view != null) {
final WindowInsets rootInsets = view.getRootWindowInsets();
if (rootInsets != null && rootInsets.isVisible(WindowInsets.Type.ime())) {
- hideSoftInputFromWindow(view.getWindowToken(), hideFlags, null,
+ hideSoftInputFromWindow(view.getWindowToken(), hideFlags,
+ null /* resultReceiver */,
SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT);
} else {
- showSoftInput(view, null /* statsToken */, showFlags, null /* resultReceiver */,
+ showSoftInput(view, showFlags, null /* resultReceiver */,
SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT);
}
}
@@ -3537,11 +3548,11 @@ public final class InputMethodManager {
@UnsupportedAppUsage
void closeCurrentInput() {
- final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
- null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION, false /* fromUser */);
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION,
+ final int reason = SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
+ ImeTracker.forLatency().onRequestHide(statsToken,
+ ImeTracker.ORIGIN_CLIENT, reason,
ActivityThread::currentApplication);
synchronized (mH) {
@@ -3562,7 +3573,7 @@ public final class InputMethodManager {
statsToken,
HIDE_NOT_ALWAYS,
null,
- SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION);
+ reason);
}
}
@@ -3603,12 +3614,12 @@ public final class InputMethodManager {
*
* @param windowToken the window from which this request originates. If this doesn't match the
* currently served view, the request is ignored and returns {@code false}.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*
* @return {@code true} if IME can (eventually) be shown, {@code false} otherwise.
* @hide
*/
- public boolean requestImeShow(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
+ public boolean requestImeShow(IBinder windowToken, @NonNull ImeTracker.Token statsToken) {
checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
@@ -3632,16 +3643,11 @@ public final class InputMethodManager {
*
* @param windowToken the window from which this request originates. If this doesn't match the
* currently served view, the request is ignored.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
* @hide
*/
- public void notifyImeHidden(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
- if (statsToken == null) {
- statsToken = ImeTracker.forLogging().onRequestHide(null /* component */,
- Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, false /* fromUser */);
- }
- ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ public void notifyImeHidden(IBinder windowToken, @NonNull ImeTracker.Token statsToken) {
+ ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT,
SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
ActivityThread::currentApplication);
ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this,
@@ -4025,8 +4031,11 @@ public final class InputMethodManager {
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) {
- InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(
- flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION);
+ final int reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
+ InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(statsToken, flags,
+ reason);
}
/**
@@ -4044,7 +4053,11 @@ public final class InputMethodManager {
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) {
- InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
+ final int reason = SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */);
+ InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(statsToken, flags,
+ reason);
}
/**
diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
index 275904347d2b..b45bc1c46967 100644
--- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -25,28 +25,19 @@ import android.view.inputmethod.ImeTracker;
interface IImeTracker {
/**
- * Called when an IME show request is created.
+ * Called when an IME request is started.
*
* @param tag the logging tag.
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME show request.
- * @param reason the reason why the IME show request was created.
+ * @param uid the uid of the client that started the request.
+ * @param type the type of the request.
+ * @param origin the origin of the request.
* @param fromUser whether this request was created directly from user interaction.
- * @return A new IME tracking token.
- */
- ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason, boolean fromUser);
-
- /**
- * Called when an IME hide request is created.
+ * @param reason the reason for starting the request.
*
- * @param tag the logging tag.
- * @param uid the uid of the client that requested the IME.
- * @param origin the origin of the IME hide request.
- * @param reason the reason why the IME hide request was created.
- * @param fromUser whether this request was created directly from user interaction.
- * @return A new IME tracking token.
+ * @return An IME request tracking token.
*/
- ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason, boolean fromUser);
+ ImeTracker.Token onStart(String tag, int uid, int type, int origin, int reason,
+ boolean fromUser);
/**
* Called when the IME request progresses to a further phase.
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index 6abd9e8a8a17..2593b78f5182 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -71,11 +71,11 @@ oneway interface IInputMethod {
void setSessionEnabled(IInputMethodSession session, boolean enabled);
- void showSoftInput(in IBinder showInputToken, in @nullable ImeTracker.Token statsToken,
- int flags, in ResultReceiver resultReceiver);
+ void showSoftInput(in IBinder showInputToken, in ImeTracker.Token statsToken, int flags,
+ in ResultReceiver resultReceiver);
- void hideSoftInput(in IBinder hideInputToken, in @nullable ImeTracker.Token statsToken,
- int flags, in ResultReceiver resultReceiver);
+ void hideSoftInput(in IBinder hideInputToken, in ImeTracker.Token statsToken, int flags,
+ in ResultReceiver resultReceiver);
void updateEditorToolType(int toolType);
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 65a2f4be9901..457b9dd34644 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -35,15 +35,17 @@ oneway interface IInputMethodPrivilegedOperations {
void setInputMethod(String id, in AndroidFuture future /* T=Void */);
void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype,
in AndroidFuture future /* T=Void */);
- void hideMySoftInput(int flags, int reason, in AndroidFuture future /* T=Void */);
- void showMySoftInput(int flags, in AndroidFuture future /* T=Void */);
+ void hideMySoftInput(in ImeTracker.Token statsToken, int flags, int reason,
+ in AndroidFuture future /* T=Void */);
+ void showMySoftInput(in ImeTracker.Token statsToken, int flags, int reason,
+ in AndroidFuture future /* T=Void */);
void updateStatusIconAsync(String packageName, int iconId);
void switchToPreviousInputMethod(in AndroidFuture future /* T=Boolean */);
void switchToNextInputMethod(boolean onlyCurrentIme, in AndroidFuture future /* T=Boolean */);
void shouldOfferSwitchingToNextInputMethod(in AndroidFuture future /* T=Boolean */);
void notifyUserActionAsync();
void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible,
- in @nullable ImeTracker.Token statsToken);
+ in ImeTracker.Token statsToken);
void onStylusHandwritingReady(int requestId, int pid);
void resetStylusHandwriting(int requestId);
void switchKeyboardLayoutAsync(int direction);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 9b7fa2f4f9e9..a0aad31d2e04 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -189,6 +189,8 @@ public final class InputMethodDebug {
*/
public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) {
switch (reason) {
+ case SoftInputShowHideReason.NOT_SET:
+ return "NOT_SET";
case SoftInputShowHideReason.SHOW_SOFT_INPUT:
return "SHOW_SOFT_INPUT";
case SoftInputShowHideReason.ATTACH_NEW_INPUT:
@@ -265,6 +267,36 @@ public final class InputMethodDebug {
return "HIDE_SOFT_INPUT_CLOSE_CURRENT_SESSION";
case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW:
return "HIDE_SOFT_INPUT_FROM_VIEW";
+ case SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT:
+ return "SHOW_SOFT_INPUT_LEGACY_DIRECT";
+ case SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT:
+ return "HIDE_SOFT_INPUT_LEGACY_DIRECT";
+ case SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT:
+ return "SHOW_WINDOW_LEGACY_DIRECT";
+ case SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT:
+ return "HIDE_WINDOW_LEGACY_DIRECT";
+ case SoftInputShowHideReason.RESET_NEW_CONFIGURATION:
+ return "RESET_NEW_CONFIGURATION";
+ case SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY:
+ return "UPDATE_CANDIDATES_VIEW_VISIBILITY";
+ case SoftInputShowHideReason.CONTROLS_CHANGED:
+ return "CONTROLS_CHANGED";
+ case SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED:
+ return "DISPLAY_CONFIGURATION_CHANGED";
+ case SoftInputShowHideReason.DISPLAY_INSETS_CHANGED:
+ return "DISPLAY_INSETS_CHANGED";
+ case SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED:
+ return "DISPLAY_CONTROLS_CHANGED";
+ case SoftInputShowHideReason.UNBIND_CURRENT_METHOD:
+ return "UNBIND_CURRENT_METHOD";
+ case SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED:
+ return "HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED";
+ case SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL:
+ return "HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL";
+ case SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT:
+ return "SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
+ case SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION:
+ return "SHOW_SOFT_INPUT_IMM_DEPRECATION";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 792388dca339..635a227e5862 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -252,20 +252,21 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)}
- *
- * @param reason the reason to hide soft input
+ * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput}
*/
@AnyThread
- public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
- @SoftInputShowHideReason int reason) {
+ public void hideMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
try {
final AndroidFuture<Void> future = new AndroidFuture<>();
- ops.hideMySoftInput(flags, reason, future);
+ ops.hideMySoftInput(statsToken, flags, reason, future);
CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -273,17 +274,21 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)}
+ * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput}
*/
@AnyThread
- public void showMySoftInput(@InputMethodManager.ShowFlags int flags) {
+ public void showMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
try {
final AndroidFuture<Void> future = new AndroidFuture<>();
- ops.showMySoftInput(flags, future);
+ ops.showMySoftInput(statsToken, flags, reason, future);
CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -379,19 +384,19 @@ public final class InputMethodPrivilegedOperations {
* {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder,
* int)}
* @param setVisible {@code true} to set IME visible, else hidden.
- * @param statsToken the token tracking the current IME request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*/
@AnyThread
public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
ImeTracker.forLogging().onFailed(statsToken,
- ImeTracker.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER);
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
return;
}
ImeTracker.forLogging().onProgress(statsToken,
- ImeTracker.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER);
+ ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS);
try {
ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible, statsToken);
} catch (RemoteException e) {
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 861b8a75f730..da738a01ec39 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -34,6 +34,7 @@ import java.lang.annotation.Retention;
*/
@Retention(SOURCE)
@IntDef(value = {
+ SoftInputShowHideReason.NOT_SET,
SoftInputShowHideReason.SHOW_SOFT_INPUT,
SoftInputShowHideReason.ATTACH_NEW_INPUT,
SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME,
@@ -72,8 +73,26 @@ import java.lang.annotation.Retention;
SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE,
SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION,
SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT,
+ SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT,
+ SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT,
+ SoftInputShowHideReason.RESET_NEW_CONFIGURATION,
+ SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY,
+ SoftInputShowHideReason.CONTROLS_CHANGED,
+ SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED,
+ SoftInputShowHideReason.DISPLAY_INSETS_CHANGED,
+ SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED,
+ SoftInputShowHideReason.UNBIND_CURRENT_METHOD,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION,
})
public @interface SoftInputShowHideReason {
+ /** Default, undefined reason. */
+ int NOT_SET = ImeProtoEnums.REASON_NOT_SET;
+
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
int SHOW_SOFT_INPUT = ImeProtoEnums.REASON_SHOW_SOFT_INPUT;
@@ -291,4 +310,91 @@ public @interface SoftInputShowHideReason {
* Hide soft input when {@link InputMethodManager#hideSoftInputFromView(View, int)} gets called.
*/
int HIDE_SOFT_INPUT_FROM_VIEW = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_FROM_VIEW;
+
+ /**
+ * Show soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService.InputMethodImpl#showSoftInput}.
+ */
+ int SHOW_SOFT_INPUT_LEGACY_DIRECT = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_LEGACY_DIRECT;
+
+ /**
+ * Hide soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService.InputMethodImpl#hideSoftInput}.
+ */
+ int HIDE_SOFT_INPUT_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_LEGACY_DIRECT;
+
+ /**
+ * Show soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService#showWindow}.
+ */
+ int SHOW_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_SHOW_WINDOW_LEGACY_DIRECT;
+
+ /**
+ * Hide soft input by legacy (discouraged) call to
+ * {@link android.inputmethodservice.InputMethodService#hideWindow}.
+ */
+ int HIDE_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_WINDOW_LEGACY_DIRECT;
+
+ /**
+ * Show / Hide soft input by
+ * {@link android.inputmethodservice.InputMethodService#resetStateForNewConfiguration}.
+ */
+ int RESET_NEW_CONFIGURATION = ImeProtoEnums.REASON_RESET_NEW_CONFIGURATION;
+
+ /**
+ * Show / Hide soft input by
+ * {@link android.inputmethodservice.InputMethodService#updateCandidatesVisibility}.
+ */
+ int UPDATE_CANDIDATES_VIEW_VISIBILITY = ImeProtoEnums.REASON_UPDATE_CANDIDATES_VIEW_VISIBILITY;
+
+ /**
+ * Show / Hide soft input by {@link android.view.InsetsController#onControlsChanged}.
+ */
+ int CONTROLS_CHANGED = ImeProtoEnums.REASON_CONTROLS_CHANGED;
+
+ /**
+ * Show soft input by
+ * {@link com.android.wm.shell.common.DisplayImeController#onDisplayConfigurationChanged}.
+ */
+ int DISPLAY_CONFIGURATION_CHANGED = ImeProtoEnums.REASON_DISPLAY_CONFIGURATION_CHANGED;
+
+ /**
+ * Show soft input by
+ * {@link com.android.wm.shell.common.DisplayImeController.PerDisplay#insetsChanged}.
+ */
+ int DISPLAY_INSETS_CHANGED = ImeProtoEnums.REASON_DISPLAY_INSETS_CHANGED;
+
+ /**
+ * Show / Hide soft input by
+ * {@link com.android.wm.shell.common.DisplayImeController.PerDisplay#insetsControlChanged}.
+ */
+ int DISPLAY_CONTROLS_CHANGED = ImeProtoEnums.REASON_DISPLAY_CONTROLS_CHANGED;
+
+ /** Hide soft input by
+ * {@link com.android.server.inputmethod.InputMethodManagerService#onUnbindCurrentMethodByReset}.
+ */
+ int UNBIND_CURRENT_METHOD = ImeProtoEnums.REASON_UNBIND_CURRENT_METHOD;
+
+ /** Hide soft input by {@link android.view.ImeInsetsSourceConsumer#onAnimationStateChanged}. */
+ int HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED =
+ ImeProtoEnums.REASON_HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED;
+
+ /** Hide soft input when we already have a {@link android.view.InsetsSourceControl} by
+ * {@link android.view.ImeInsetsSourceConsumer#requestHide}.
+ */
+ int HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL =
+ ImeProtoEnums.REASON_HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL;
+
+ /**
+ * Show soft input by
+ * {@link android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int)}.
+ */
+ int SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT =
+ ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT;
+
+ /**
+ * Show soft input by the deprecated
+ * {@link InputMethodManager#showSoftInputFromInputMethod(IBinder, int)}.
+ */
+ int SHOW_SOFT_INPUT_IMM_DEPRECATION = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IMM_DEPRECATION;
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1f4503a69428..dc3b5a8846cf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -65,12 +65,21 @@ interface IInputMethodManager {
InputMethodSubtype getLastInputMethodSubtype(int userId);
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
- in @nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
+ in ImeTracker.Token statsToken, int flags, int lastClickToolType,
in @nullable ResultReceiver resultReceiver, int reason);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
- in @nullable ImeTracker.Token statsToken, int flags,
+ in ImeTracker.Token statsToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);
+ /**
+ * A test API for CTS to request hiding the current soft input window, with the request origin
+ * on the server side.
+ */
+ @EnforcePermission("TEST_INPUT_METHOD")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.TEST_INPUT_METHOD)")
+ void hideSoftInputFromServerForTest();
+
// TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'editorInfo' is non-null then also does startInput.
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index cb281ff8a6a7..b9a12ad57c33 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -32,6 +32,7 @@ import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
@@ -86,7 +87,8 @@ public class PowerManagerTest {
// Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
@Rule
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood() ? null
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
: DeviceFlagsValueProvider.createCheckFlagsRule();
/**
diff --git a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
index c70da6e94385..fcdc5905ef88 100644
--- a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
+++ b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
@@ -22,6 +22,7 @@ import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.runner.AndroidJUnit4;
@@ -40,7 +41,8 @@ public class WorkDurationUnitTest {
// Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
@Rule
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood() ? null
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
: DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 8c93fbbc6b47..48ba52621f64 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -24,8 +24,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.AdditionalMatchers.and;
+import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -36,6 +38,7 @@ import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
+import android.view.inputmethod.ImeTracker;
import android.widget.TextView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -96,12 +99,12 @@ public class ImeInsetsSourceConsumerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// test if setVisibility can show IME
mImeConsumer.onWindowFocusGained(true);
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertTrue((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
// test if setVisibility can hide IME
- mController.hide(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.hide(WindowInsets.Type.ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertFalse((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
});
@@ -114,8 +117,9 @@ public class ImeInsetsSourceConsumerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
+ final var statsToken = ImeTracker.Token.empty();
mImeConsumer.onWindowFocusGained(true);
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
// set control and verify visibility is applied.
InsetsSourceControl control = new InsetsSourceControl(ID_IME,
@@ -124,10 +128,10 @@ public class ImeInsetsSourceConsumerTest {
// IME show animation should be triggered when control becomes available.
verify(mController).applyAnimation(
eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
- any() /* statsToken */);
+ eq(statsToken));
verify(mController, never()).applyAnimation(
eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */,
- any() /* statsToken */);
+ eq(statsToken));
});
}
@@ -152,9 +156,9 @@ public class ImeInsetsSourceConsumerTest {
// Request IME visible before control is available.
mImeConsumer.onWindowFocusGained(hasWindowFocus);
final boolean imeVisible = hasWindowFocus && hasViewFocus;
+ final var statsToken = ImeTracker.Token.empty();
if (imeVisible) {
- mController.show(WindowInsets.Type.ime(), true /* fromIme */,
- null /* statsToken */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
}
// set control and verify visibility is applied.
@@ -168,23 +172,25 @@ public class ImeInsetsSourceConsumerTest {
// and expect skip animation state after getAndClearSkipAnimationOnce invoked.
mController.onControlsChanged(new InsetsSourceControl[]{ control });
verify(control).getAndClearSkipAnimationOnce();
+ // This ends up creating a new request when we gain control,
+ // so the statsToken won't match.
verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
eq(true) /* show */, eq(false) /* fromIme */,
- eq(expectSkipAnim) /* skipAnim */, eq(null) /* statsToken */);
+ eq(expectSkipAnim) /* skipAnim */, and(not(eq(statsToken)), notNull()));
}
// If previously hasViewFocus is false, verify when requesting the IME visible next
// time will not skip animation.
if (!hasViewFocus) {
- mController.show(WindowInsets.Type.ime(), true /* fromIme */,
- null /* statsToken */);
+ final var statsTokenNext = ImeTracker.Token.empty();
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsTokenNext);
mController.onControlsChanged(new InsetsSourceControl[]{ control });
// Verify IME show animation should be triggered when control becomes available and
// the animation will be skipped by getAndClearSkipAnimationOnce invoked.
verify(control).getAndClearSkipAnimationOnce();
verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
eq(true) /* show */, eq(true) /* fromIme */,
- eq(false) /* skipAnim */, eq(null) /* statsToken */);
+ eq(false) /* skipAnim */, eq(statsTokenNext));
}
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 1568174e1955..316e191eecbd 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -256,7 +256,7 @@ public class InsetsControllerTest {
mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
// When using the animation thread, this must not invoke onReady()
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
});
@@ -273,14 +273,14 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.show(all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimations();
- final @InsetsType int types = navigationBars() | statusBars() | ime();
+ @InsetsType final int types = navigationBars() | statusBars() | ime();
assertEquals(types, mController.getRequestedVisibleTypes() & types);
- mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+ mController.hide(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.hide(all());
mController.cancelExistingAnimations();
assertEquals(0, mController.getRequestedVisibleTypes() & types);
@@ -295,10 +295,10 @@ public class InsetsControllerTest {
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
- mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertTrue(isRequestedVisible(mController, ime()));
- mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+ mController.hide(ime(), true /* fromIme */, ImeTracker.Token.empty());
mController.cancelExistingAnimations();
assertFalse(isRequestedVisible(mController, ime()));
mController.getSourceConsumer(ID_IME, ime()).onWindowFocusLost();
@@ -465,7 +465,7 @@ public class InsetsControllerTest {
assertFalse(mController.getState().peekSource(ID_IME).isVisible());
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
// Gaining control shortly after
mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
@@ -489,7 +489,7 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
mController.cancelExistingAnimations();
@@ -567,7 +567,7 @@ public class InsetsControllerTest {
verify(listener, never()).onReady(any(), anyInt());
// Pretend that IME is calling.
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -651,7 +651,7 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
copy.peekSource(ID_IME).setFrame(0, 1, 2, 3);
@@ -851,7 +851,7 @@ public class InsetsControllerTest {
// Showing invisible ime should only causes insets change once.
clearInvocations(mTestHost);
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
verify(mTestHost, times(1)).notifyInsetsChanged();
// Sending the same insets state should not cause insets change.
@@ -918,7 +918,7 @@ public class InsetsControllerTest {
assertNull(imeInsetsConsumer.getControl());
// Verify IME requested visibility should be updated to IME consumer from controller.
- mController.show(ime(), true /* fromIme */, null /* statsToken */);
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
assertTrue(isRequestedVisible(mController, ime()));
mController.hide(ime());
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 2f2da8c53db0..b53b9c519cb6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -387,7 +387,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// Sets the dim area when the two TaskFragments are adjacent.
final boolean dimOnTask = !isStacked
- && splitAttributes.getWindowAttributes().getDimArea() == DIM_AREA_ON_TASK
+ && splitAttributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
&& Flags.fullscreenDimFlag();
setTaskFragmentDimOnTask(wct, primaryContainer.getTaskFragmentToken(), dimOnTask);
setTaskFragmentDimOnTask(wct, secondaryContainer.getTaskFragmentToken(), dimOnTask);
@@ -590,7 +590,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final boolean isFillParent = relativeBounds.isEmpty();
final boolean isIsolatedNavigated = !isFillParent && container.isOverlay();
final boolean dimOnTask = !isFillParent
- && attributes.getWindowAttributes().getDimArea() == DIM_AREA_ON_TASK
+ && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
&& Flags.fullscreenDimFlag();
final IBinder fragmentToken = container.getTaskFragmentToken();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 2ea43162d225..ad01d0fa311a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -20,12 +20,12 @@ import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_CANCEL;
import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_END;
import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_START;
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
-import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.res.Configuration;
@@ -51,6 +51,7 @@ import android.view.inputmethod.InputMethodManagerGlobal;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
@@ -122,7 +123,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
- pd.startAnimation(true, false /* forceRestart */, null /* statsToken */);
+ pd.startAnimation(true, false /* forceRestart */,
+ SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED);
}
}
@@ -257,7 +259,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mInsetsState.set(insetsState, true /* copySources */);
if (mImeShowing && !Objects.equals(oldFrame, newFrame) && newSourceVisible) {
if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
- startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
+ startAnimation(mImeShowing, true /* forceRestart */,
+ SoftInputShowHideReason.DISPLAY_INSETS_CHANGED);
}
}
@@ -291,7 +294,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
final boolean positionChanged =
!imeSourceControl.getSurfacePosition().equals(lastSurfacePosition);
if (positionChanged) {
- startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
+ startAnimation(mImeShowing, true /* forceRestart */,
+ SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED);
}
} else {
if (!haveSameLeash(mImeSourceControl, imeSourceControl)) {
@@ -384,7 +388,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
private void startAnimation(final boolean show, final boolean forceRestart,
- @Nullable ImeTracker.Token statsToken) {
+ @SoftInputShowHideReason int reason) {
+ final var imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
+ if (imeSource == null || mImeSourceControl == null) {
+ return;
+ }
+ final var statsToken = ImeTracker.forLogging().onStart(
+ show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_WM_SHELL,
+ reason, false /* fromUser */);
+
+ startAnimation(show, forceRestart, statsToken);
+ }
+
+ private void startAnimation(final boolean show, final boolean forceRestart,
+ @NonNull final ImeTracker.Token statsToken) {
final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
if (imeSource == null || mImeSourceControl == null) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
@@ -458,7 +475,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
- @Nullable
+ @NonNull
private final ImeTracker.Token mStatsToken = statsToken;
@Override
@@ -484,7 +501,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
- statsToken != null ? statsToken.getTag() : TOKEN_NONE,
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, alpha, startY , endY,
Objects.toString(mImeSourceControl.getLeash()),
Objects.toString(mImeSourceControl.getInsetsHint()),
@@ -500,7 +517,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mCancelled = true;
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL,
- statsToken != null ? statsToken.getTag() : TOKEN_NONE, mDisplayId,
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
+ mDisplayId,
Objects.toString(mImeSourceControl.getInsetsHint()));
}
}
@@ -528,7 +546,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
- statsToken != null ? statsToken.getTag() : TOKEN_NONE,
+ mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
mDisplayId, mAnimationDirection, endY,
Objects.toString(mImeSourceControl.getLeash()),
Objects.toString(mImeSourceControl.getInsetsHint()),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
index 9bdda14cf00b..ca06024a9adb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -277,8 +277,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
*
* @param types {@link InsetsType} to show
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME show request
- * or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void showInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {}
@@ -288,8 +287,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
*
* @param types {@link InsetsType} to hide
* @param fromIme true if this request originated from IME (InputMethodService).
- * @param statsToken the token tracking the current IME hide request
- * or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void hideInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 01e2f988fbfc..2c0aa12f22d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -38,6 +38,7 @@ import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.view.inputmethod.ImeTracker;
import androidx.test.filters.SmallTest;
@@ -51,6 +52,12 @@ import org.mockito.MockitoAnnotations;
import java.util.concurrent.Executor;
+/**
+ * Tests for the display IME controller.
+ *
+ * <p> Build/Install/Run:
+ * atest WMShellUnitTests:DisplayImeControllerTest
+ */
@SmallTest
public class DisplayImeControllerTest extends ShellTestCase {
@@ -99,13 +106,13 @@ public class DisplayImeControllerTest extends ShellTestCase {
@Test
public void showInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.showInsets(ime(), true /* fromIme */, null /* statsToken */);
+ mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
verifyZeroInteractions(mExecutor);
}
@Test
public void hideInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.hideInsets(ime(), true /* fromIme */, null /* statsToken */);
+ mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
verifyZeroInteractions(mExecutor);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
index 956f1cd419c2..669e433ba386 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
@@ -50,6 +50,12 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
+/**
+ * Tests for the display insets controller.
+ *
+ * <p> Build/Install/Run:
+ * atest WMShellUnitTests:DisplayInsetsControllerTest
+ */
@SmallTest
public class DisplayInsetsControllerTest extends ShellTestCase {
@@ -114,9 +120,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
@@ -136,9 +142,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false,
- null /* statsToken */);
+ ImeTracker.Token.empty());
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
deleted file mode 100644
index eb1faa0aa25c..000000000000
--- a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Type annotations for constants used in the connectivity API surface.
- *
- * The annotations are maintained in a separate class so that it can be built as
- * a separate library that other modules can build against, as Typedef should not
- * be exposed as SystemApi.
- *
- * @hide
- */
-public final class ConnectivityAnnotations {
- private ConnectivityAnnotations() {}
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER,
- ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY,
- ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE,
- })
- public @interface MultipathPreference {}
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = false, value = {
- ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED,
- ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED,
- ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED,
- })
- public @interface RestrictBackgroundStatus {}
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 61c3ce7f6988..c2c82b35317b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1764,40 +1764,4 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
boolean getUnpairing() {
return mUnpairing;
}
-
- ListenableFuture<Void> syncProfileForMemberDevice() {
- return ThreadUtils.getBackgroundExecutor()
- .submit(
- () -> {
- List<Pair<LocalBluetoothProfile, Boolean>> toSync =
- Stream.of(
- mProfileManager.getA2dpProfile(),
- mProfileManager.getHeadsetProfile(),
- mProfileManager.getHearingAidProfile(),
- mProfileManager.getLeAudioProfile(),
- mProfileManager.getLeAudioBroadcastAssistantProfile())
- .filter(Objects::nonNull)
- .map(profile -> new Pair<>(profile, profile.isEnabled(mDevice)))
- .toList();
-
- for (var t : toSync) {
- LocalBluetoothProfile profile = t.first;
- boolean enabledForMain = t.second;
-
- for (var member : mMemberDevices) {
- BluetoothDevice btDevice = member.getDevice();
-
- if (enabledForMain != profile.isEnabled(btDevice)) {
- Log.d(TAG, "Syncing profile " + profile + " to "
- + enabledForMain + " for member device "
- + btDevice.getAnonymizedAddress() + " of main device "
- + mDevice.getAnonymizedAddress());
- profile.setEnabled(btDevice, enabledForMain);
- }
- }
- }
- return null;
- }
- );
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 32eec7e709af..4e52c77f27b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -363,7 +363,6 @@ public class CachedBluetoothDeviceManager {
if (profileId == BluetoothProfile.HEADSET
|| profileId == BluetoothProfile.A2DP
|| profileId == BluetoothProfile.LE_AUDIO
- || profileId == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
|| profileId == BluetoothProfile.CSIP_SET_COORDINATOR) {
return mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
state);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index e67ec48d3401..a49314aae1b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -379,7 +379,6 @@ public class CsipDeviceManager {
if (hasChanged) {
log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: "
+ mCachedDevices);
- preferredMainDevice.syncProfileForMemberDevice();
}
return hasChanged;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index ca47efdc5df3..1069b715d946 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -346,11 +346,15 @@ public class HearingAidDeviceManager {
} else {
long hiSyncId = asha.getHiSyncId(cachedDevice.getDevice());
if (isValidHiSyncId(hiSyncId)) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ final HearingAidInfo info = new HearingAidInfo.Builder()
.setAshaDeviceSide(asha.getDeviceSide(cachedDevice.getDevice()))
.setAshaDeviceMode(asha.getDeviceMode(cachedDevice.getDevice()))
- .setHiSyncId(hiSyncId);
- return infoBuilder.build();
+ .setHiSyncId(hiSyncId)
+ .build();
+ if (DEBUG) {
+ Log.d(TAG, "generateHearingAidInfo, " + cachedDevice + ", info=" + info);
+ }
+ return info;
}
}
@@ -358,15 +362,20 @@ public class HearingAidDeviceManager {
final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
if (hapClientProfile == null || leAudioProfile == null) {
Log.w(TAG, "HapClientProfile or LeAudioProfile is not supported on this device");
- } else {
+ } else if (cachedDevice.getProfiles().stream().anyMatch(
+ p -> p instanceof HapClientProfile)) {
int audioLocation = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
int hearingAidType = hapClientProfile.getHearingAidType(cachedDevice.getDevice());
if (audioLocation != BluetoothLeAudio.AUDIO_LOCATION_INVALID
&& hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ final HearingAidInfo info = new HearingAidInfo.Builder()
.setLeAudioLocation(audioLocation)
- .setHapDeviceType(hearingAidType);
- return infoBuilder.build();
+ .setHapDeviceType(hearingAidType)
+ .build();
+ if (DEBUG) {
+ Log.d(TAG, "generateHearingAidInfo, " + cachedDevice + ", info=" + info);
+ }
+ return info;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
index 7d614f0e4d39..65b73caeada7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -15,6 +15,7 @@
*/
package com.android.settingslib.wifi
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
@@ -22,14 +23,23 @@ import android.icu.text.MessageFormat
import android.net.wifi.ScanResult
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus
+import android.net.wifi.WifiManager
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
import com.android.settingslib.R
import com.android.settingslib.flags.Flags.newStatusBarIcons
import java.util.Locale
+import kotlin.coroutines.resume
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
open class WifiUtils {
@@ -146,7 +156,6 @@ open class WifiUtils {
}
}
-
@JvmStatic
fun buildLoggingSummary(accessPoint: AccessPoint, config: WifiConfiguration?): String {
val summary = StringBuilder()
@@ -458,5 +467,40 @@ open class WifiUtils {
arguments["count"] = connectedDevices
return msgFormat.format(arguments)
}
+
+ @JvmStatic
+ fun checkWepAllowed(
+ context: Context,
+ lifecycleOwner: LifecycleOwner,
+ ssid: String,
+ onAllowed: () -> Unit,
+ ) {
+ lifecycleOwner.lifecycleScope.launch {
+ val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
+ if (wifiManager.queryWepAllowed()) {
+ onAllowed()
+ } else {
+ val intent = Intent(Intent.ACTION_MAIN).apply {
+ component = ComponentName(
+ "com.android.settings",
+ "com.android.settings.network.WepNetworkDialogActivity"
+ )
+ putExtra(SSID, ssid)
+ }
+ context.startActivity(intent)
+ }
+ }
+ }
+
+ private suspend fun WifiManager.queryWepAllowed(): Boolean =
+ withContext(Dispatchers.Default) {
+ suspendCancellableCoroutine { continuation ->
+ queryWepAllowed(Dispatchers.Default.asExecutor()) {
+ continuation.resume(it)
+ }
+ }
+ }
+
+ const val SSID = "ssid"
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 461ecf5d3c84..5996dbb322fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -18,9 +18,7 @@ package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -58,8 +56,6 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-import java.util.concurrent.ExecutionException;
-
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class CachedBluetoothDeviceTest {
@@ -1823,52 +1819,6 @@ public class CachedBluetoothDeviceTest {
assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
}
- @Test
- public void syncProfileForMemberDevice_hasDiff_shouldSync()
- throws ExecutionException, InterruptedException {
- mCachedDevice.addMemberDevice(mSubCachedDevice);
- when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
- when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
- when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
-
- when(mA2dpProfile.isEnabled(mDevice)).thenReturn(true);
- when(mHearingAidProfile.isEnabled(mDevice)).thenReturn(true);
- when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
-
- when(mA2dpProfile.isEnabled(mSubDevice)).thenReturn(true);
- when(mHearingAidProfile.isEnabled(mSubDevice)).thenReturn(false);
- when(mLeAudioProfile.isEnabled(mSubDevice)).thenReturn(false);
-
- mCachedDevice.syncProfileForMemberDevice().get();
-
- verify(mA2dpProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- verify(mHearingAidProfile).setEnabled(any(BluetoothDevice.class), eq(true));
- verify(mLeAudioProfile).setEnabled(any(BluetoothDevice.class), eq(true));
- }
-
- @Test
- public void syncProfileForMemberDevice_noDiff_shouldNotSync()
- throws ExecutionException, InterruptedException {
- mCachedDevice.addMemberDevice(mSubCachedDevice);
- when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
- when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
- when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
-
- when(mA2dpProfile.isEnabled(mDevice)).thenReturn(false);
- when(mHearingAidProfile.isEnabled(mDevice)).thenReturn(false);
- when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
-
- when(mA2dpProfile.isEnabled(mSubDevice)).thenReturn(false);
- when(mHearingAidProfile.isEnabled(mSubDevice)).thenReturn(false);
- when(mLeAudioProfile.isEnabled(mSubDevice)).thenReturn(true);
-
- mCachedDevice.syncProfileForMemberDevice().get();
-
- verify(mA2dpProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- verify(mHearingAidProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- verify(mLeAudioProfile, never()).setEnabled(any(BluetoothDevice.class), anyBoolean());
- }
-
private HearingAidInfo getLeftAshaHearingAidInfo() {
return new HearingAidInfo.Builder()
.setAshaDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT)
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
deleted file mode 100644
index 76931a2b4d41..000000000000
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.compose
-
-import android.content.Context
-import android.graphics.Point
-import android.view.View
-import android.view.WindowInsets
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.LifecycleOwner
-import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.compose.theme.PlatformTheme
-import com.android.compose.ui.platform.DensityAwareComposeView
-import com.android.internal.policy.ScreenDecorationsUtils
-import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.composable.BouncerContent
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
-import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
-import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
-import com.android.systemui.communal.ui.compose.CommunalContainer
-import com.android.systemui.communal.ui.compose.CommunalHub
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView
-import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
-import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.people.ui.compose.PeopleScreen
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import com.android.systemui.qs.footer.ui.compose.FooterActions
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.scene.shared.model.Scene
-import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.ui.composable.ComposableScene
-import com.android.systemui.scene.ui.composable.SceneContainer
-import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
-import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-
-/** The Compose facade, when Compose is available. */
-object ComposeFacade : BaseComposeFacade {
- override fun isComposeAvailable(): Boolean = true
-
- override fun composeInitializer(): ComposeInitializer = ComposeInitializerImpl
-
- override fun setPeopleSpaceActivityContent(
- activity: ComponentActivity,
- viewModel: PeopleViewModel,
- onResult: (PeopleViewModel.Result) -> Unit,
- ) {
- activity.setContent { PlatformTheme { PeopleScreen(viewModel, onResult) } }
- }
-
- override fun setCommunalEditWidgetActivityContent(
- activity: ComponentActivity,
- viewModel: BaseCommunalViewModel,
- widgetConfigurator: WidgetConfigurator,
- onOpenWidgetPicker: () -> Unit,
- onEditDone: () -> Unit,
- ) {
- activity.setContent {
- PlatformTheme {
- Box(
- modifier =
- Modifier.fillMaxSize()
- .background(LocalAndroidColorScheme.current.outlineVariant),
- ) {
- CommunalHub(
- viewModel = viewModel,
- onOpenWidgetPicker = onOpenWidgetPicker,
- widgetConfigurator = widgetConfigurator,
- onEditDone = onEditDone,
- )
- }
- }
- }
- }
-
- override fun setVolumePanelActivityContent(
- activity: ComponentActivity,
- viewModel: VolumePanelViewModel,
- onDismiss: () -> Unit,
- ) {
- activity.setContent {
- VolumePanelRoot(
- viewModel = viewModel,
- onDismiss = onDismiss,
- )
- }
- }
-
- override fun createFooterActionsView(
- context: Context,
- viewModel: FooterActionsViewModel,
- qsVisibilityLifecycleOwner: LifecycleOwner,
- ): View {
- return DensityAwareComposeView(context).apply {
- setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
- }
- }
-
- override fun createSceneContainerView(
- scope: CoroutineScope,
- context: Context,
- viewModel: SceneContainerViewModel,
- windowInsets: StateFlow<WindowInsets?>,
- sceneByKey: Map<SceneKey, Scene>,
- dataSourceDelegator: SceneDataSourceDelegator,
- ): View {
- return ComposeView(context).apply {
- setContent {
- PlatformTheme {
- ScreenDecorProvider(
- displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
- screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
- ) {
- SceneContainer(
- viewModel = viewModel,
- sceneByKey =
- sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
- dataSourceDelegator = dataSourceDelegator,
- )
- }
- }
- }
- }
- }
-
- override fun createStickyKeysIndicatorContent(
- context: Context,
- viewModel: StickyKeysIndicatorViewModel
- ): View {
- return createStickyKeyIndicatorView(context, viewModel)
- }
-
- override fun createCommunalView(
- context: Context,
- viewModel: BaseCommunalViewModel,
- ): View {
- return ComposeView(context).apply {
- setContent { PlatformTheme { CommunalHub(viewModel = viewModel) } }
- }
- }
-
- override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
- return ComposeView(context).apply {
- setContent { PlatformTheme { CommunalContainer(viewModel = viewModel) } }
- }
- }
-
- // TODO(b/298525212): remove once Compose exposes window inset bounds.
- private fun displayCutoutFromWindowInsets(
- scope: CoroutineScope,
- context: Context,
- windowInsets: StateFlow<WindowInsets?>,
- ): StateFlow<DisplayCutout> =
- windowInsets
- .map {
- val boundingRect = it?.displayCutout?.boundingRectTop
- val width = boundingRect?.let { boundingRect.right - boundingRect.left } ?: 0
- val left = boundingRect?.left?.toDp(context) ?: 0.dp
- val top = boundingRect?.top?.toDp(context) ?: 0.dp
- val right = boundingRect?.right?.toDp(context) ?: 0.dp
- val bottom = boundingRect?.bottom?.toDp(context) ?: 0.dp
- val location =
- when {
- width <= 0f -> CutoutLocation.NONE
- left <= 0.dp -> CutoutLocation.LEFT
- right >= getDisplayWidth(context) -> CutoutLocation.RIGHT
- else -> CutoutLocation.CENTER
- }
- DisplayCutout(
- left,
- top,
- right,
- bottom,
- location,
- )
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout())
-
- // TODO(b/298525212): remove once Compose exposes window inset bounds.
- private fun getDisplayWidth(context: Context): Dp {
- val point = Point()
- checkNotNull(context.display).getRealSize(point)
- return point.x.dp
- }
-
- // TODO(b/298525212): remove once Compose exposes window inset bounds.
- private fun Int.toDp(context: Context): Dp {
- return (this.toFloat() / context.resources.displayMetrics.density).dp
- }
-
- override fun createBouncer(
- context: Context,
- viewModel: BouncerViewModel,
- dialogFactory: BouncerDialogFactory,
- ): View {
- return ComposeView(context).apply {
- setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
- }
- }
-
- override fun createLockscreen(
- context: Context,
- viewModel: LockscreenContentViewModel,
- blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
- ): View {
- val sceneBlueprints =
- blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet()
- return ComposeView(context).apply {
- setContent {
- LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints)
- .Content(modifier = Modifier.fillMaxSize())
- }
- }
- }
-}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt
deleted file mode 100644
index 1674591c30b5..000000000000
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt
+++ /dev/null
@@ -1,77 +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.compose
-
-import android.view.View
-import androidx.lifecycle.findViewTreeLifecycleOwner
-import androidx.lifecycle.setViewTreeLifecycleOwner
-import androidx.lifecycle.Lifecycle
-import androidx.savedstate.SavedStateRegistryController
-import androidx.savedstate.SavedStateRegistryOwner
-import com.android.compose.animation.ViewTreeSavedStateRegistryOwner
-import com.android.systemui.lifecycle.ViewLifecycleOwner
-
-internal object ComposeInitializerImpl : ComposeInitializer {
- override fun onAttachedToWindow(root: View) {
- if (root.findViewTreeLifecycleOwner() != null) {
- error("root $root already has a LifecycleOwner")
- }
-
- val parent = root.parent
- if (parent is View && parent.id != android.R.id.content) {
- error(
- "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." +
- "Outside of activities and dialogs, this is usually the top-most View of a " +
- "window."
- )
- }
-
- // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is
- // both visible and focused.
- val lifecycleOwner = ViewLifecycleOwner(root)
-
- // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save
- // or restore because SystemUI process is always running and top-level windows using this
- // initializer are created once, when the process is started.
- val savedStateRegistryOwner =
- object : SavedStateRegistryOwner {
- private val savedStateRegistryController =
- SavedStateRegistryController.create(this).apply { performRestore(null) }
-
- override val savedStateRegistry = savedStateRegistryController.savedStateRegistry
-
- override val lifecycle: Lifecycle
- get() = lifecycleOwner.lifecycle
- }
-
- // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner]
- // because `onCreate` might move the lifecycle state to STARTED which will make
- // [SavedStateRegistryController.performRestore] throw.
- lifecycleOwner.onCreate()
-
- // Set the owners on the root. They will be reused by any ComposeView inside the root
- // hierarchy.
- root.setViewTreeLifecycleOwner(lifecycleOwner)
- ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner)
- }
-
- override fun onDetachedFromWindow(root: View) {
- (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy()
- root.setViewTreeLifecycleOwner(null)
- ViewTreeSavedStateRegistryOwner.set(root, null)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
index 4c972e9195e9..a3371d3d24f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
@@ -42,6 +42,25 @@ class AodToGoneTransitionViewModelTest : SysuiTestCase() {
val underTest = kosmos.aodToGoneTransitionViewModel
@Test
+ fun lockscreenAlpha() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor(alpha = { 0.5f })
+ val alpha by collectValues(underTest.lockscreenAlpha(viewState))
+
+ repository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ assertThat(alpha[0]).isEqualTo(0.5f)
+ // Fades out just prior to halfway
+ assertThat(alpha[1]).isEqualTo(0f)
+ // Must finish at 0
+ assertThat(alpha[2]).isEqualTo(0f)
+ }
+
+ @Test
fun deviceEntryParentViewHides() =
testScope.runTest {
val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
index 7e937db842ff..79671b885c56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
@@ -51,6 +51,25 @@ class DozingToGoneTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ fun lockscreenAlpha() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor(alpha = { 0.6f })
+ val alpha by collectValues(underTest.lockscreenAlpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ assertThat(alpha[0]).isEqualTo(0.6f)
+ // Fades out just prior to halfway
+ assertThat(alpha[1]).isEqualTo(0f)
+ // Must finish at 0
+ assertThat(alpha[2]).isEqualTo(0f)
+ }
+
+ @Test
fun deviceEntryParentViewDisappear() =
testScope.runTest {
val values by collectValues(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 503fd34ce2c2..8e15b5d5657f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -328,4 +328,42 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
shadeRepository.setQsExpansion(0.5f)
assertThat(alpha).isEqualTo(0f)
}
+
+ @Test
+ fun alpha_idleOnOccluded_isZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+ assertThat(alpha).isEqualTo(1f)
+
+ // Go to OCCLUDED state
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope = testScope,
+ )
+ assertThat(alpha).isEqualTo(0f)
+
+ // Try pulling down shade and ensure the value doesn't change
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun alpha_idleOnGone_isZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+ assertThat(alpha).isEqualTo(1f)
+
+ // Go to GONE state
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+ assertThat(alpha).isEqualTo(0f)
+
+ // Try pulling down shade and ensure the value doesn't change
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index c01f1c71fdd3..8aa0e3fc4d23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -35,10 +35,10 @@ import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeAnimationRepository
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -76,7 +76,7 @@ class ActivityStarterImplTest : SysuiTestCase() {
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var keyguardViewMediator: KeyguardViewMediator
@Mock private lateinit var shadeController: ShadeController
- @Mock private lateinit var shadeViewController: ShadeViewController
+ @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
@Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@@ -105,7 +105,7 @@ class ActivityStarterImplTest : SysuiTestCase() {
Lazy { biometricUnlockController },
Lazy { keyguardViewMediator },
Lazy { shadeController },
- Lazy { shadeViewController },
+ commandQueue,
shadeAnimationInteractor,
Lazy { statusBarKeyguardViewManager },
Lazy { notifShadeWindowController },
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index 36d3ed52b655..f1a0e5e3539c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -11,7 +11,6 @@ import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.Flags.COMPOSE_BOUNCER_ENABLED
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -60,11 +59,7 @@ constructor(
private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>,
) {
fun bind(view: ViewGroup) {
- if (
- COMPOSE_BOUNCER_ENABLED &&
- composeBouncerFlags.isOnlyComposeBouncerEnabled() &&
- ComposeFacade.isComposeAvailable()
- ) {
+ if (COMPOSE_BOUNCER_ENABLED && composeBouncerFlags.isOnlyComposeBouncerEnabled()) {
val deps = composeBouncerDependencies.get()
ComposeBouncerViewBinder.bind(
view,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index 7b053956091e..179fa874db79 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -1,15 +1,17 @@
package com.android.systemui.bouncer.ui.binder
import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.theme.PlatformTheme
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.composable.BouncerContent
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.flow.collectLatest
@@ -27,12 +29,11 @@ object ComposeBouncerViewBinder {
viewMediatorCallback: ViewMediatorCallback?,
) {
view.addView(
- ComposeFacade.createBouncer(
- view.context,
- viewModel,
- dialogFactory,
- )
+ ComposeView(view.context).apply {
+ setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
+ }
)
+
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index a5a390d7683b..48b3e4c3616d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -25,14 +25,21 @@ import android.util.Log
import android.view.IWindowManager
import android.view.WindowInsets
import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.compose.theme.PlatformTheme
import com.android.internal.logging.UiEventLogger
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
-import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
@@ -110,56 +117,68 @@ constructor(
val preselectedKey = intent.getStringExtra(EXTRA_PRESELECTED_KEY)
communalViewModel.setSelectedKey(preselectedKey)
- setCommunalEditWidgetActivityContent(
- activity = this,
- viewModel = communalViewModel,
- widgetConfigurator = widgetConfigurator,
- onOpenWidgetPicker = {
- val intent =
- Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) }
- packageManager
- .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
- ?.activityInfo
- ?.packageName
- ?.let { packageName ->
- try {
- addWidgetActivityLauncher.launch(
- Intent(Intent.ACTION_PICK).apply {
- setPackage(packageName)
- putExtra(
- EXTRA_DESIRED_WIDGET_WIDTH,
- resources.getDimensionPixelSize(
- R.dimen.communal_widget_picker_desired_width
- )
- )
- putExtra(
- EXTRA_DESIRED_WIDGET_HEIGHT,
- resources.getDimensionPixelSize(
- R.dimen.communal_widget_picker_desired_height
- )
- )
- putExtra(
- AppWidgetManager.EXTRA_CATEGORY_FILTER,
- communalViewModel.getCommunalWidgetCategories
- )
- }
+ setContent {
+ PlatformTheme {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .background(LocalAndroidColorScheme.current.outlineVariant),
+ ) {
+ CommunalHub(
+ viewModel = communalViewModel,
+ onOpenWidgetPicker = ::onOpenWidgetPicker,
+ widgetConfigurator = widgetConfigurator,
+ onEditDone = ::onEditDone,
+ )
+ }
+ }
+ }
+ }
+
+ private fun onOpenWidgetPicker() {
+ val intent = Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) }
+ packageManager
+ .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ ?.activityInfo
+ ?.packageName
+ ?.let { packageName ->
+ try {
+ addWidgetActivityLauncher.launch(
+ Intent(Intent.ACTION_PICK).apply {
+ setPackage(packageName)
+ putExtra(
+ EXTRA_DESIRED_WIDGET_WIDTH,
+ resources.getDimensionPixelSize(
+ R.dimen.communal_widget_picker_desired_width
+ )
+ )
+ putExtra(
+ EXTRA_DESIRED_WIDGET_HEIGHT,
+ resources.getDimensionPixelSize(
+ R.dimen.communal_widget_picker_desired_height
+ )
+ )
+ putExtra(
+ AppWidgetManager.EXTRA_CATEGORY_FILTER,
+ communalViewModel.getCommunalWidgetCategories
)
- } catch (e: Exception) {
- Log.e(TAG, "Failed to launch widget picker activity", e)
}
- }
- ?: run { Log.e(TAG, "Couldn't resolve launcher package name") }
- },
- onEditDone = {
- try {
- communalViewModel.onSceneChanged(CommunalSceneKey.Communal)
- checkNotNull(windowManagerService).lockNow(/* options */ null)
- finish()
- } catch (e: RemoteException) {
- Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to launch widget picker activity", e)
}
}
- )
+ ?: run { Log.e(TAG, "Couldn't resolve launcher package name") }
+ }
+
+ private fun onEditDone() {
+ try {
+ communalViewModel.onSceneChanged(CommunalSceneKey.Communal)
+ checkNotNull(windowManagerService).lockNow(/* options */ null)
+ finish()
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
+ }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
deleted file mode 100644
index a0aaa906802a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.compose
-
-import android.content.Context
-import android.view.View
-import android.view.WindowInsets
-import androidx.activity.ComponentActivity
-import androidx.lifecycle.LifecycleOwner
-import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
-import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.scene.shared.model.Scene
-import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.StateFlow
-
-/**
- * A facade to interact with Compose, when it is available.
- *
- * You should access this facade by calling the static methods on
- * [com.android.systemui.compose.ComposeFacade] directly.
- */
-interface BaseComposeFacade {
- /**
- * Whether Compose is currently available. This function should be checked before calling any
- * other functions on this facade.
- *
- * This value will never change at runtime.
- */
- fun isComposeAvailable(): Boolean
-
- /**
- * Return the [ComposeInitializer] to make Compose usable in windows outside normal activities.
- */
- fun composeInitializer(): ComposeInitializer
-
- /** Bind the content of [activity] to [viewModel]. */
- fun setPeopleSpaceActivityContent(
- activity: ComponentActivity,
- viewModel: PeopleViewModel,
- onResult: (PeopleViewModel.Result) -> Unit,
- )
-
- /** Bind the content of [activity] to [viewModel]. */
- fun setCommunalEditWidgetActivityContent(
- activity: ComponentActivity,
- viewModel: BaseCommunalViewModel,
- widgetConfigurator: WidgetConfigurator,
- onOpenWidgetPicker: () -> Unit,
- onEditDone: () -> Unit,
- )
-
- fun setVolumePanelActivityContent(
- activity: ComponentActivity,
- viewModel: VolumePanelViewModel,
- onDismiss: () -> Unit,
- )
-
- /** Create a [View] to represent [viewModel] on screen. */
- fun createFooterActionsView(
- context: Context,
- viewModel: FooterActionsViewModel,
- qsVisibilityLifecycleOwner: LifecycleOwner,
- ): View
-
- /** Create a [View] to represent [viewModel] on screen. */
- fun createSceneContainerView(
- scope: CoroutineScope,
- context: Context,
- viewModel: SceneContainerViewModel,
- windowInsets: StateFlow<WindowInsets?>,
- sceneByKey: Map<SceneKey, Scene>,
- dataSourceDelegator: SceneDataSourceDelegator,
- ): View
-
- /** Creates sticky key indicator content presenting provided [viewModel] */
- fun createStickyKeysIndicatorContent(
- context: Context,
- viewModel: StickyKeysIndicatorViewModel
- ): View
-
- /** Create a [View] to represent [viewModel] on screen. */
- fun createCommunalView(
- context: Context,
- viewModel: BaseCommunalViewModel,
- ): View
-
- /** Create a [View] to represent the [BouncerViewModel]. */
- fun createBouncer(
- context: Context,
- viewModel: BouncerViewModel,
- dialogFactory: BouncerDialogFactory,
- ): View
-
- /** Creates a container that hosts the communal UI and handles gesture transitions. */
- fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View
-
- /** Creates a [View] that represents the Lockscreen. */
- fun createLockscreen(
- context: Context,
- viewModel: LockscreenContentViewModel,
- blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
- ): View
-}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
index 90dc3a00daa2..813e0e025bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt
@@ -17,6 +17,13 @@
package com.android.systemui.compose
import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.findViewTreeLifecycleOwner
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import com.android.compose.animation.ViewTreeSavedStateRegistryOwner
+import com.android.systemui.lifecycle.ViewLifecycleOwner
/**
* An initializer to use Compose outside of an Activity, e.g. inside a window added directly using
@@ -39,10 +46,55 @@ import android.view.View
* }
* ```
*/
-interface ComposeInitializer {
+object ComposeInitializer {
/** Function to be called on your window root view's [View.onAttachedToWindow] function. */
- fun onAttachedToWindow(root: View)
+ fun onAttachedToWindow(root: View) {
+ if (root.findViewTreeLifecycleOwner() != null) {
+ error("root $root already has a LifecycleOwner")
+ }
+
+ val parent = root.parent
+ if (parent is View && parent.id != android.R.id.content) {
+ error(
+ "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." +
+ "Outside of activities and dialogs, this is usually the top-most View of a " +
+ "window."
+ )
+ }
+
+ // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is
+ // both visible and focused.
+ val lifecycleOwner = ViewLifecycleOwner(root)
+
+ // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save
+ // or restore because SystemUI process is always running and top-level windows using this
+ // initializer are created once, when the process is started.
+ val savedStateRegistryOwner =
+ object : SavedStateRegistryOwner {
+ private val savedStateRegistryController =
+ SavedStateRegistryController.create(this).apply { performRestore(null) }
+
+ override val savedStateRegistry = savedStateRegistryController.savedStateRegistry
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleOwner.lifecycle
+ }
+
+ // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner]
+ // because `onCreate` might move the lifecycle state to STARTED which will make
+ // [SavedStateRegistryController.performRestore] throw.
+ lifecycleOwner.onCreate()
+
+ // Set the owners on the root. They will be reused by any ComposeView inside the root
+ // hierarchy.
+ root.setViewTreeLifecycleOwner(lifecycleOwner)
+ ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner)
+ }
/** Function to be called on your window root view's [View.onDetachedFromWindow] function. */
- fun onDetachedFromWindow(root: View)
+ fun onDetachedFromWindow(root: View) {
+ (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy()
+ root.setViewTreeLifecycleOwner(null)
+ ViewTreeSavedStateRegistryOwner.set(root, null)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
index 3ed58a7fe5ae..f9084e5a6191 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
@@ -26,9 +26,9 @@ import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
import androidx.activity.ComponentDialog
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView
import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
import com.android.systemui.res.R
import javax.inject.Inject
@@ -48,7 +48,7 @@ constructor(
return ComponentDialog(context, R.style.Theme_SystemUI_Dialog).apply {
// because we're requesting window feature it must be called before setting content
window?.setStickyKeyWindowAttributes()
- setContentView(ComposeFacade.createStickyKeysIndicatorContent(context, viewModel))
+ setContentView(createStickyKeyIndicatorView(context, viewModel))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
index 842fd04bfcc5..78c4e77cad74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
@@ -17,15 +17,13 @@
package com.android.systemui.keyboard.stickykeys.ui
import android.app.Dialog
-import android.util.Log
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
-import javax.inject.Inject
@SysUISingleton
class StickyKeysIndicatorCoordinator
@@ -40,11 +38,6 @@ constructor(
private var dialog: Dialog? = null
fun startListening() {
- // this check needs to be moved to PhysicalKeyboardCoreStartable
- if (!ComposeFacade.isComposeAvailable()) {
- Log.e("StickyKeysIndicatorCoordinator", "Compose is required for this UI")
- return
- }
applicationScope.launch {
viewModel.indicatorContent.collect { stickyKeys ->
stickyKeysLogger.logNewUiState(stickyKeys)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 301942f6242b..106fdf1fbcbe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,6 +20,9 @@ package com.android.systemui.keyguard
import android.content.Context
import android.view.LayoutInflater
import android.view.View
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
@@ -35,7 +38,6 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
@@ -45,6 +47,8 @@ import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
+import com.android.systemui.keyguard.ui.composable.LockscreenContent
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
@@ -132,7 +136,7 @@ constructor(
if (!SceneContainerFlag.isEnabled) {
if (ComposeLockscreen.isEnabled) {
val composeView =
- ComposeFacade.createLockscreen(
+ createLockscreen(
context = context,
viewModel = lockscreenContentViewModel,
blueprints = lockscreenSceneBlueprintsLazy.get(),
@@ -207,6 +211,21 @@ constructor(
)
}
+ private fun createLockscreen(
+ context: Context,
+ viewModel: LockscreenContentViewModel,
+ blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+ ): View {
+ val sceneBlueprints =
+ blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet()
+ return ComposeView(context).apply {
+ setContent {
+ LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints)
+ .Content(modifier = Modifier.fillMaxSize())
+ }
+ }
+ }
+
/**
* Temporary, to allow NotificationPanelViewController to use the same instance while code is
* migrated: b/288242803
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index e0b5c0e1f4c6..617982f8532c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -27,12 +27,14 @@ import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
@SysUISingleton
@@ -45,6 +47,7 @@ constructor(
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
+ private val powerInteractor: PowerInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.AOD,
@@ -68,15 +71,16 @@ constructor(
*/
private fun listenForAodToOccluded() {
scope.launch {
- keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect {
- (isOccluded, startedKeyguardState) ->
- if (isOccluded && startedKeyguardState == KeyguardState.AOD) {
- startTransitionTo(
- toState = KeyguardState.OCCLUDED,
- modeOnCanceled = TransitionModeOnCanceled.RESET
- )
+ keyguardInteractor.isKeyguardOccluded
+ .sample(startedKeyguardTransitionStep, ::Pair)
+ .collect { (isOccluded, lastStartedStep) ->
+ if (isOccluded && lastStartedStep.to == KeyguardState.AOD) {
+ startTransitionTo(
+ toState = KeyguardState.OCCLUDED,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ }
}
- }
}
}
@@ -85,15 +89,18 @@ constructor(
keyguardInteractor
.dozeTransitionTo(DozeStateModel.FINISH)
.sample(
+ keyguardInteractor.isKeyguardShowing,
startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
keyguardInteractor.biometricUnlockState,
)
- .collect { (_, lastStartedStep, occluded, biometricUnlockState) ->
+ .collect { (_, isKeyguardShowing, lastStartedStep, occluded, biometricUnlockState)
+ ->
if (
lastStartedStep.to == KeyguardState.AOD &&
!occluded &&
- !isWakeAndUnlock(biometricUnlockState)
+ !isWakeAndUnlock(biometricUnlockState) &&
+ isKeyguardShowing
) {
val modeOnCanceled =
if (lastStartedStep.from == KeyguardState.LOCKSCREEN) {
@@ -134,13 +141,31 @@ constructor(
}
scope.launch {
- keyguardInteractor.biometricUnlockState.sample(finishedKeyguardState, ::Pair).collect {
- (biometricUnlockState, keyguardState) ->
- KeyguardWmStateRefactor.assertInLegacyMode()
- if (keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)) {
- startTransitionTo(KeyguardState.GONE)
+ powerInteractor.isAwake
+ .debounce(50L)
+ .sample(
+ keyguardInteractor.biometricUnlockState,
+ startedKeyguardTransitionStep,
+ keyguardInteractor.isKeyguardShowing,
+ keyguardInteractor.isKeyguardDismissible,
+ )
+ .collect {
+ (
+ isAwake,
+ biometricUnlockState,
+ lastStartedTransitionStep,
+ isKeyguardShowing,
+ isKeyguardDismissible) ->
+ KeyguardWmStateRefactor.assertInLegacyMode()
+ if (
+ isAwake &&
+ lastStartedTransitionStep.to == KeyguardState.AOD &&
+ (isWakeAndUnlock(biometricUnlockState) ||
+ (!isKeyguardShowing && isKeyguardDismissible))
+ ) {
+ startTransitionTo(KeyguardState.GONE)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 54d5908e9fa4..baa865d22f70 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -26,13 +26,12 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.toQuad
-import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.kotlin.Utils.Companion.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
@SysUISingleton
@@ -56,50 +55,47 @@ constructor(
) {
override fun start() {
- listenForDozingToLockscreenHubOrOccluded()
- listenForDozingToGone()
+ listenForDozingToAny()
listenForTransitionToCamera(scope, keyguardInteractor)
}
- private fun listenForDozingToLockscreenHubOrOccluded() {
+ private fun listenForDozingToAny() {
scope.launch {
powerInteractor.isAwake
+ .debounce(50L)
.sample(
- combine(
- startedKeyguardTransitionStep,
- keyguardInteractor.isKeyguardOccluded,
- communalInteractor.isIdleOnCommunal,
- ::Triple
- ),
- ::toQuad
+ keyguardInteractor.biometricUnlockState,
+ startedKeyguardTransitionStep,
+ keyguardInteractor.isKeyguardOccluded,
+ communalInteractor.isIdleOnCommunal,
+ keyguardInteractor.isKeyguardShowing,
+ keyguardInteractor.isKeyguardDismissible,
)
- .collect { (isAwake, lastStartedTransition, occluded, isIdleOnCommunal) ->
- if (isAwake && lastStartedTransition.to == KeyguardState.DOZING) {
- startTransitionTo(
- if (occluded) {
- KeyguardState.OCCLUDED
- } else if (isIdleOnCommunal) {
- KeyguardState.GLANCEABLE_HUB
- } else {
- KeyguardState.LOCKSCREEN
- }
- )
- }
- }
- }
- }
-
- private fun listenForDozingToGone() {
- scope.launch {
- keyguardInteractor.biometricUnlockState
- .sample(startedKeyguardTransitionStep, ::Pair)
- .collect { (biometricUnlockState, lastStartedTransition) ->
- if (
- lastStartedTransition.to == KeyguardState.DOZING &&
- isWakeAndUnlock(biometricUnlockState)
- ) {
- startTransitionTo(KeyguardState.GONE)
+ .collect {
+ (
+ isAwake,
+ biometricUnlockState,
+ lastStartedTransition,
+ occluded,
+ isIdleOnCommunal,
+ isKeyguardShowing,
+ isKeyguardDismissible) ->
+ if (!(isAwake && lastStartedTransition.to == KeyguardState.DOZING)) {
+ return@collect
}
+ startTransitionTo(
+ if (isWakeAndUnlock(biometricUnlockState)) {
+ KeyguardState.GONE
+ } else if (isKeyguardDismissible && !isKeyguardShowing) {
+ KeyguardState.GONE
+ } else if (occluded) {
+ KeyguardState.OCCLUDED
+ } else if (isIdleOnCommunal) {
+ KeyguardState.GLANCEABLE_HUB
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index a03fa38ec850..d81f1f14158c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -21,6 +21,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -37,6 +39,8 @@ constructor(
private val keyguardInteractor: KeyguardInteractor,
private val logger: KeyguardLogger,
private val powerInteractor: PowerInteractor,
+ private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val shadeInteractor: ShadeInteractor,
) {
fun start() {
@@ -47,6 +51,30 @@ constructor(
}
scope.launch {
+ sharedNotificationContainerViewModel
+ .getMaxNotifications { height, useExtraShelfSpace -> height.toInt() }
+ .collect { logger.log(TAG, VERBOSE, "Notif: max height in px", it) }
+ }
+
+ scope.launch {
+ sharedNotificationContainerViewModel.isOnLockscreen.collect {
+ logger.log(TAG, VERBOSE, "Notif: isOnLockscreen", it)
+ }
+ }
+
+ scope.launch {
+ shadeInteractor.isUserInteracting.collect {
+ logger.log(TAG, VERBOSE, "Shade: isUserInteracting", it)
+ }
+ }
+
+ scope.launch {
+ sharedNotificationContainerViewModel.isOnLockscreenWithoutShade.collect {
+ logger.log(TAG, VERBOSE, "Notif: isOnLockscreenWithoutShade", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.primaryBouncerShowing.collect {
logger.log(TAG, VERBOSE, "Primary bouncer showing", it)
}
@@ -75,6 +103,18 @@ constructor(
}
scope.launch {
+ keyguardInteractor.isKeyguardDismissible.collect {
+ logger.log(TAG, VERBOSE, "isDismissible", it)
+ }
+ }
+
+ scope.launch {
+ keyguardInteractor.isKeyguardShowing.collect {
+ logger.log(TAG, VERBOSE, "isShowing", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.dozeTransitionModel.collect {
logger.log(TAG, VERBOSE, "Doze transition", it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt
index 7f0b483919b3..601fbfaf1b64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.shared
import com.android.systemui.Flags
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
@@ -34,7 +33,7 @@ object ComposeLockscreen {
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.composeLockscreen() && ComposeFacade.isComposeAvailable()
+ get() = Flags.composeLockscreen()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index b92a9a08987a..a9eec18319c3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -16,13 +16,16 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
/** Breaks down AOD->GONE transition into discrete steps for corresponding views to consume. */
@ExperimentalCoroutinesApi
@@ -40,5 +43,15 @@ constructor(
to = KeyguardState.GONE,
)
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var startAlpha = 1f
+ return transitionAnimation.sharedFlow(
+ duration = 200.milliseconds,
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 0f, it) },
+ onFinish = { 0f },
+ )
+ }
+
override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index fca1604946e1..8851a51f15b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -16,12 +16,14 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -41,6 +43,16 @@ constructor(
to = KeyguardState.GONE,
)
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var startAlpha = 1f
+ return transitionAnimation.sharedFlow(
+ duration = 200.milliseconds,
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 0f, it) },
+ onFinish = { 0f },
+ )
+ }
+
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index bdcaf0951c5b..38d5e0f74b28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
@@ -70,7 +71,9 @@ constructor(
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
+ private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
@@ -120,6 +123,27 @@ constructor(
}
.distinctUntilChanged()
+ /**
+ * Keyguard should not show while the communal hub is fully visible. This check is added since
+ * at the moment, closing the notification shade will cause the keyguard alpha to be set back to
+ * 1. Also ensure keyguard is never visible when GONE.
+ */
+ private val hideKeyguard: Flow<Boolean> =
+ combine(
+ communalInteractor.isIdleOnCommunal,
+ keyguardTransitionInteractor
+ .transitionValue(GONE)
+ .map { it == 1f }
+ .onStart { emit(false) },
+ keyguardTransitionInteractor
+ .transitionValue(OCCLUDED)
+ .map { it == 1f }
+ .onStart { emit(false) },
+ ) { isIdleOnCommunal, isGone, isOccluded ->
+ isIdleOnCommunal || isGone || isOccluded
+ }
+ .distinctUntilChanged()
+
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
@@ -136,19 +160,16 @@ constructor(
/** An observable for the alpha level for the entire keyguard root view. */
fun alpha(viewState: ViewStateAccessor): Flow<Float> {
return combine(
- communalInteractor.isIdleOnCommunal,
- keyguardTransitionInteractor
- .transitionValue(GONE)
- .map { it == 1f }
- .onStart { emit(false) }
- .distinctUntilChanged(),
+ hideKeyguard,
// The transitions are mutually exclusive, so they are safe to merge to get the last
// value emitted by any of them. Do not add flows that cannot make this guarantee.
merge(
alphaOnShadeExpansion,
keyguardInteractor.dismissAlpha.filterNotNull(),
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
+ aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
+ dozingToGoneTransitionViewModel.lockscreenAlpha(viewState),
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
goneToAodTransitionViewModel.enterFromTopAnimationAlpha,
@@ -167,12 +188,8 @@ constructor(
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
)
.onStart { emit(1f) }
- ) { isIdleOnCommunal, gone, alpha ->
- if (isIdleOnCommunal || gone) {
- // Keyguard should not show while the communal hub is fully visible. This check
- // is added since at the moment, closing the notification shade will cause the
- // keyguard alpha to be set back to 1. Also ensure keyguard is never visible
- // when GONE.
+ ) { hideKeyguard, alpha ->
+ if (hideKeyguard) {
0f
} else {
alpha
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 8b7c85b65824..f2013bec19c7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -546,7 +546,7 @@ public class LogModule {
@SysUISingleton
@KeyguardLog
public static LogBuffer provideKeyguardLogBuffer(LogBufferFactory factory) {
- return factory.create("KeyguardLog", 250);
+ return factory.create("KeyguardLog", 500);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
index f1cade7512e2..0b19bab5c7c5 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
@@ -24,12 +24,14 @@ import android.media.projection.ReviewGrantedConsentResult
import android.os.RemoteException
import android.os.ServiceManager
import android.util.Log
+import android.window.WindowContainerToken
+import javax.inject.Inject
/**
* Helper class that handles the media projection service related actions. It simplifies invoking
* the MediaProjectionManagerService and updating the permission consent.
*/
-class MediaProjectionServiceHelper {
+class MediaProjectionServiceHelper @Inject constructor() {
companion object {
private const val TAG = "MediaProjectionServiceHelper"
private val service =
@@ -90,4 +92,16 @@ class MediaProjectionServiceHelper {
}
}
}
+
+ /** Updates the projected task to the task that has a matching [WindowContainerToken]. */
+ fun updateTaskRecordingSession(token: WindowContainerToken): Boolean {
+ return try {
+ true
+ // TODO: actually call the service once it is implemented
+ // service.updateTaskRecordingSession(token)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Unable to updateTaskRecordingSession", e)
+ false
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
index 9938f11e5d4c..cfbcaf91b791 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
@@ -16,11 +16,11 @@
package com.android.systemui.mediaprojection.taskswitcher.data.model
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
/** Represents the state of media projection. */
sealed interface MediaProjectionState {
object NotProjecting : MediaProjectionState
object EntireScreen : MediaProjectionState
- data class SingleTask(val task: TaskInfo) : MediaProjectionState
+ data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
index 492d482459d6..4ff54d4eae65 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
@@ -17,10 +17,14 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityOptions
+import android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.app.ActivityTaskManager
+import android.app.IActivityTaskManager
import android.app.TaskStackListener
import android.os.IBinder
import android.util.Log
+import android.view.Display
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -40,11 +44,24 @@ import kotlinx.coroutines.withContext
class ActivityTaskManagerTasksRepository
@Inject
constructor(
- private val activityTaskManager: ActivityTaskManager,
+ private val activityTaskManager: IActivityTaskManager,
@Application private val applicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : TasksRepository {
+ override suspend fun launchRecentTask(taskInfo: RunningTaskInfo) {
+ withContext(backgroundDispatcher) {
+ val activityOptions = ActivityOptions.makeBasic()
+ activityOptions.pendingIntentBackgroundActivityStartMode =
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ activityOptions.launchDisplayId = taskInfo.displayId
+ activityTaskManager.startActivityFromRecents(
+ taskInfo.taskId,
+ activityOptions.toBundle()
+ )
+ }
+ }
+
override suspend fun findRunningTaskFromWindowContainerToken(
windowContainerToken: IBinder
): RunningTaskInfo? =
@@ -53,7 +70,14 @@ constructor(
}
private suspend fun getRunningTasks(): List<RunningTaskInfo> =
- withContext(backgroundDispatcher) { activityTaskManager.getTasks(Integer.MAX_VALUE) }
+ withContext(backgroundDispatcher) {
+ activityTaskManager.getTasks(
+ /* maxNum = */ Integer.MAX_VALUE,
+ /* filterForVisibleRecents = */ false,
+ /* keepIntentExtra = */ false,
+ /* displayId = */ Display.INVALID_DISPLAY
+ )
+ }
override val foregroundTask: Flow<RunningTaskInfo> =
conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
index 6480a47e8ea2..74d19921c706 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
+import android.app.ActivityManager.RunningTaskInfo
import android.media.projection.MediaProjectionInfo
import android.media.projection.MediaProjectionManager
import android.os.Handler
@@ -26,15 +27,19 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
@SysUISingleton
class MediaProjectionManagerRepository
@@ -43,9 +48,21 @@ constructor(
private val mediaProjectionManager: MediaProjectionManager,
@Main private val handler: Handler,
@Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
private val tasksRepository: TasksRepository,
+ private val mediaProjectionServiceHelper: MediaProjectionServiceHelper,
) : MediaProjectionRepository {
+ override suspend fun switchProjectedTask(task: RunningTaskInfo) {
+ withContext(backgroundDispatcher) {
+ if (mediaProjectionServiceHelper.updateTaskRecordingSession(task.token)) {
+ Log.d(TAG, "Successfully switched projected task")
+ } else {
+ Log.d(TAG, "Failed to switch projected task")
+ }
+ }
+ }
+
override val mediaProjectionState: Flow<MediaProjectionState> =
conflatedCallbackFlow {
val callback =
@@ -82,7 +99,9 @@ constructor(
}
val matchingTask =
tasksRepository.findRunningTaskFromWindowContainerToken(
- checkNotNull(session.tokenToRecord)) ?: return MediaProjectionState.EntireScreen
+ checkNotNull(session.tokenToRecord)
+ )
+ ?: return MediaProjectionState.EntireScreen
return MediaProjectionState.SingleTask(matchingTask)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
index 5bec6925babe..e495466008ce 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
@@ -16,12 +16,16 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
+import android.app.ActivityManager.RunningTaskInfo
import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
import kotlinx.coroutines.flow.Flow
/** Represents a repository to retrieve and change data related to media projection. */
interface MediaProjectionRepository {
+ /** Switches the task that should be projected. */
+ suspend fun switchProjectedTask(task: RunningTaskInfo)
+
/** Represents the current [MediaProjectionState]. */
val mediaProjectionState: Flow<MediaProjectionState>
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt
deleted file mode 100644
index 544eb6b99d4f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/NoOpMediaProjectionRepository.kt
+++ /dev/null
@@ -1,33 +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.mediaprojection.taskswitcher.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-
-/**
- * No-op implementation of [MediaProjectionRepository] that does nothing. Currently used as a
- * placeholder, while the real implementation is not completed.
- */
-@SysUISingleton
-class NoOpMediaProjectionRepository @Inject constructor() : MediaProjectionRepository {
-
- override val mediaProjectionState: Flow<MediaProjectionState> = emptyFlow()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt
index 6a535e4ecc50..9ef42b4de45c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/TasksRepository.kt
@@ -23,6 +23,8 @@ import kotlinx.coroutines.flow.Flow
/** Repository responsible for retrieving data related to running tasks. */
interface TasksRepository {
+ suspend fun launchRecentTask(taskInfo: RunningTaskInfo)
+
/**
* Tries to find a [RunningTaskInfo] with a matching window container token. Returns `null` when
* no matching task was found.
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
index fc5cf7d75bdf..eb9e6a5de057 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.domain.interactor
+import android.app.ActivityManager.RunningTaskInfo
import android.app.TaskInfo
import android.content.Intent
import android.util.Log
@@ -37,10 +38,18 @@ import kotlinx.coroutines.flow.map
class TaskSwitchInteractor
@Inject
constructor(
- mediaProjectionRepository: MediaProjectionRepository,
+ private val mediaProjectionRepository: MediaProjectionRepository,
private val tasksRepository: TasksRepository,
) {
+ suspend fun switchProjectedTask(task: RunningTaskInfo) {
+ mediaProjectionRepository.switchProjectedTask(task)
+ }
+
+ suspend fun goBackToTask(task: RunningTaskInfo) {
+ tasksRepository.launchRecentTask(task)
+ }
+
/**
* Emits a stream of changes to the state of task switching, in the context of media projection.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt
index cd1258ed6aa8..caabc64efae1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/model/TaskSwitchState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.domain.model
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
/** Represents tha state of task switching in the context of single task media projection. */
sealed interface TaskSwitchState {
@@ -25,6 +25,8 @@ sealed interface TaskSwitchState {
/** The foreground task is the same as the task that is currently being projected. */
object TaskUnchanged : TaskSwitchState
/** The foreground task is a different one to the task it currently being projected. */
- data class TaskSwitched(val projectedTask: TaskInfo, val foregroundTask: TaskInfo) :
- TaskSwitchState
+ data class TaskSwitched(
+ val projectedTask: RunningTaskInfo,
+ val foregroundTask: RunningTaskInfo
+ ) : TaskSwitchState
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
index 7840da960a83..dab7439f0f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt
@@ -16,23 +16,25 @@
package com.android.systemui.mediaprojection.taskswitcher.ui
+import android.app.ActivityManager.RunningTaskInfo
import android.app.Notification
-import android.app.NotificationChannel
import android.app.NotificationManager
+import android.app.PendingIntent
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Parcelable
import android.util.Log
-import com.android.systemui.res.R
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.NotShowing
import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.Showing
import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel
+import com.android.systemui.res.R
import com.android.systemui.util.NotificationChannels
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
/** Coordinator responsible for showing/hiding the task switcher notification. */
@@ -43,32 +45,54 @@ constructor(
private val context: Context,
private val notificationManager: NotificationManager,
@Application private val applicationScope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
private val viewModel: TaskSwitcherNotificationViewModel,
+ private val broadcastDispatcher: BroadcastDispatcher,
) {
+
fun start() {
applicationScope.launch {
- viewModel.uiState.flowOn(mainDispatcher).collect { uiState ->
- Log.d(TAG, "uiState -> $uiState")
- when (uiState) {
- is Showing -> showNotification()
- is NotShowing -> hideNotification()
+ launch {
+ viewModel.uiState.collect { uiState ->
+ Log.d(TAG, "uiState -> $uiState")
+ when (uiState) {
+ is Showing -> showNotification(uiState)
+ is NotShowing -> hideNotification()
+ }
}
}
+ launch {
+ broadcastDispatcher
+ .broadcastFlow(IntentFilter(SWITCH_ACTION)) { intent, _ ->
+ intent.requireParcelableExtra<RunningTaskInfo>(EXTRA_ACTION_TASK)
+ }
+ .collect { task: RunningTaskInfo ->
+ Log.d(TAG, "Switch action triggered: $task")
+ viewModel.onSwitchTaskClicked(task)
+ }
+ }
+ launch {
+ broadcastDispatcher
+ .broadcastFlow(IntentFilter(GO_BACK_ACTION)) { intent, _ ->
+ intent.requireParcelableExtra<RunningTaskInfo>(EXTRA_ACTION_TASK)
+ }
+ .collect { task ->
+ Log.d(TAG, "Go back action triggered: $task")
+ viewModel.onGoBackToTaskClicked(task)
+ }
+ }
}
}
- private fun showNotification() {
- notificationManager.notify(TAG, NOTIFICATION_ID, createNotification())
+ private fun showNotification(uiState: Showing) {
+ notificationManager.notify(TAG, NOTIFICATION_ID, createNotification(uiState))
}
- private fun createNotification(): Notification {
- // TODO(b/286201261): implement actions
+ private fun createNotification(uiState: Showing): Notification {
val actionSwitch =
Notification.Action.Builder(
/* icon = */ null,
context.getString(R.string.media_projection_task_switcher_action_switch),
- /* intent = */ null
+ createActionPendingIntent(action = SWITCH_ACTION, task = uiState.foregroundTask)
)
.build()
@@ -76,34 +100,40 @@ constructor(
Notification.Action.Builder(
/* icon = */ null,
context.getString(R.string.media_projection_task_switcher_action_back),
- /* intent = */ null
+ createActionPendingIntent(action = GO_BACK_ACTION, task = uiState.projectedTask)
)
.build()
-
- val channel =
- NotificationChannel(
- NotificationChannels.HINTS,
- context.getString(R.string.media_projection_task_switcher_notification_channel),
- NotificationManager.IMPORTANCE_HIGH
- )
- notificationManager.createNotificationChannel(channel)
- return Notification.Builder(context, channel.id)
+ return Notification.Builder(context, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.qs_screen_record_icon_on)
.setAutoCancel(true)
.setContentText(context.getString(R.string.media_projection_task_switcher_text))
.addAction(actionSwitch)
.addAction(actionBack)
- .setPriority(Notification.PRIORITY_HIGH)
- .setDefaults(Notification.DEFAULT_VIBRATE)
.build()
}
private fun hideNotification() {
- notificationManager.cancel(NOTIFICATION_ID)
+ notificationManager.cancel(TAG, NOTIFICATION_ID)
}
+ private fun createActionPendingIntent(action: String, task: RunningTaskInfo) =
+ PendingIntent.getBroadcast(
+ context,
+ /* requestCode= */ 0,
+ Intent(action).apply { putExtra(EXTRA_ACTION_TASK, task) },
+ /* flags= */ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+
companion object {
private const val TAG = "TaskSwitchNotifCoord"
private const val NOTIFICATION_ID = 5566
+
+ private const val EXTRA_ACTION_TASK = "extra_task"
+
+ private const val SWITCH_ACTION = "com.android.systemui.mediaprojection.SWITCH_TASK"
+ private const val GO_BACK_ACTION = "com.android.systemui.mediaprojection.GO_BACK"
}
}
+
+private fun <T : Parcelable> Intent.requireParcelableExtra(key: String) =
+ getParcelableExtra<T>(key)!!
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt
index 21aee72d17ae..f307761a1875 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/model/TaskSwitcherNotificationUiState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher.ui.model
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
/** Represents the UI state for the task switcher notification. */
sealed interface TaskSwitcherNotificationUiState {
@@ -24,7 +24,7 @@ sealed interface TaskSwitcherNotificationUiState {
object NotShowing : TaskSwitcherNotificationUiState
/** The notification should be shown. */
data class Showing(
- val projectedTask: TaskInfo,
- val foregroundTask: TaskInfo,
+ val projectedTask: RunningTaskInfo,
+ val foregroundTask: RunningTaskInfo,
) : TaskSwitcherNotificationUiState
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt
index d9754d4429d4..cc8cc5165e4f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModel.kt
@@ -16,15 +16,24 @@
package com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel
+import android.app.ActivityManager.RunningTaskInfo
import android.util.Log
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
import com.android.systemui.mediaprojection.taskswitcher.domain.model.TaskSwitchState
import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
-class TaskSwitcherNotificationViewModel @Inject constructor(interactor: TaskSwitchInteractor) {
+class TaskSwitcherNotificationViewModel
+@Inject
+constructor(
+ private val interactor: TaskSwitchInteractor,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
val uiState: Flow<TaskSwitcherNotificationUiState> =
interactor.taskSwitchChanges.map { taskSwitchChange ->
@@ -43,6 +52,13 @@ class TaskSwitcherNotificationViewModel @Inject constructor(interactor: TaskSwit
}
}
+ suspend fun onSwitchTaskClicked(task: RunningTaskInfo) {
+ interactor.switchProjectedTask(task)
+ }
+
+ suspend fun onGoBackToTaskClicked(task: RunningTaskInfo) =
+ withContext(backgroundDispatcher) { interactor.goBackToTask(task) }
+
companion object {
private const val TAG = "TaskSwitchNotifVM"
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
index 5b7eb454597c..deb0fed0ffc8 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
@@ -20,14 +20,15 @@ import android.appwidget.AppWidgetManager
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
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.compose.theme.PlatformTheme
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.people.ui.compose.PeopleScreen
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
@@ -65,13 +66,11 @@ constructor(
}
// Set the content of the activity, using either the View or Compose implementation.
- if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE) && isComposeAvailable()) {
+ if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE)) {
Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity")
- setPeopleSpaceActivityContent(
- activity = this,
- viewModel,
- onResult = { finishActivity(it) },
- )
+ setContent {
+ PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) }
+ }
} else {
Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity")
val view = PeopleViewBinder.create(this)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 741336277119..a000d63a2ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -47,7 +47,6 @@ import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ShadeInterpolation;
-import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -301,8 +300,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
private void bindFooterActionsView(View root) {
LinearLayout footerActionsView = root.findViewById(R.id.qs_footer_actions);
- if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)
- || !ComposeFacade.INSTANCE.isComposeAvailable()) {
+ if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)) {
Log.d(TAG, "Binding the View implementation of the QS footer actions");
mFooterActionsView = footerActionsView;
mFooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
@@ -312,7 +310,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
// Compose is available, so let's use the Compose implementation of the footer actions.
Log.d(TAG, "Binding the Compose implementation of the QS footer actions");
- View composeView = ComposeFacade.INSTANCE.createFooterActionsView(root.getContext(),
+ View composeView = QSUtils.createFooterActionsView(root.getContext(),
mQSFooterActionsViewModel, mListeningAndVisibilityLifecycleOwner);
mFooterActionsView = composeView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
index e42264f24e92..15c3f271469d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
@@ -1,7 +1,13 @@
package com.android.systemui.qs
import android.content.Context
+import android.view.View
+import androidx.lifecycle.LifecycleOwner
+import com.android.compose.theme.PlatformTheme
+import com.android.compose.ui.platform.DensityAwareComposeView
import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.qs.footer.ui.compose.FooterActions
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.util.LargeScreenUtils.shouldUseLargeScreenShadeHeader
object QSUtils {
@@ -21,4 +27,15 @@ object QSUtils {
SystemBarUtils.getQuickQsOffsetHeight(context)
}
}
-} \ No newline at end of file
+
+ @JvmStatic
+ fun createFooterActionsView(
+ context: Context,
+ viewModel: FooterActionsViewModel,
+ qsVisibilityLifecycleOwner: LifecycleOwner,
+ ): View {
+ return DensityAwareComposeView(context).apply {
+ setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 8408c51c86dc..1808d98cd692 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -24,7 +24,6 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.Flags.sceneContainer
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED
@@ -47,9 +46,8 @@ object SceneContainerFlag {
keyguardBottomAreaRefactor() &&
migrateClocksToBlueprint() &&
ComposeLockscreen.isEnabled &&
- MediaInSceneContainerFlag.isEnabled &&
- // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
- ComposeFacade.isComposeAvailable()
+ MediaInSceneContainerFlag.isEnabled
+ // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
/**
* The main static flag, SCENE_CONTAINER_ENABLED. This is an explicit static flag check that
@@ -74,11 +72,7 @@ object SceneContainerFlag {
/** The full set of requirements for SceneContainer */
inline fun getAllRequirements(): Sequence<FlagToken> {
- val composeRequirement =
- FlagToken("ComposeFacade.isComposeAvailable()", ComposeFacade.isComposeAvailable())
- return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) +
- getSecondaryFlags() +
- composeRequirement
+ return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) + getSecondaryFlags()
}
/** Return all dependencies of this flag in pairs where [Pair.first] depends on [Pair.second] */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index ee76c0582b9d..f2697b4e1c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -16,17 +16,26 @@
package com.android.systemui.scene.ui.view
+import android.content.Context
+import android.graphics.Point
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.compose.ComposeFacade
+import com.android.compose.theme.PlatformTheme
+import com.android.internal.policy.ScreenDecorationsUtils
+import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
+import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
+import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlags
@@ -34,10 +43,16 @@ import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.SceneContainer
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
object SceneWindowRootViewBinder {
@@ -83,7 +98,7 @@ object SceneWindowRootViewBinder {
)
view.addView(
- ComposeFacade.createSceneContainerView(
+ createSceneContainerView(
scope = this,
context = view.context,
viewModel = viewModel,
@@ -120,4 +135,74 @@ object SceneWindowRootViewBinder {
}
}
}
+
+ private fun createSceneContainerView(
+ scope: CoroutineScope,
+ context: Context,
+ viewModel: SceneContainerViewModel,
+ windowInsets: StateFlow<WindowInsets?>,
+ sceneByKey: Map<SceneKey, Scene>,
+ dataSourceDelegator: SceneDataSourceDelegator,
+ ): View {
+ return ComposeView(context).apply {
+ setContent {
+ PlatformTheme {
+ ScreenDecorProvider(
+ displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
+ screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+ ) {
+ SceneContainer(
+ viewModel = viewModel,
+ sceneByKey =
+ sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
+ dataSourceDelegator = dataSourceDelegator,
+ )
+ }
+ }
+ }
+ }
+ }
+
+ // TODO(b/298525212): remove once Compose exposes window inset bounds.
+ private fun displayCutoutFromWindowInsets(
+ scope: CoroutineScope,
+ context: Context,
+ windowInsets: StateFlow<WindowInsets?>,
+ ): StateFlow<DisplayCutout> =
+ windowInsets
+ .map {
+ val boundingRect = it?.displayCutout?.boundingRectTop
+ val width = boundingRect?.let { boundingRect.right - boundingRect.left } ?: 0
+ val left = boundingRect?.left?.toDp(context) ?: 0.dp
+ val top = boundingRect?.top?.toDp(context) ?: 0.dp
+ val right = boundingRect?.right?.toDp(context) ?: 0.dp
+ val bottom = boundingRect?.bottom?.toDp(context) ?: 0.dp
+ val location =
+ when {
+ width <= 0f -> CutoutLocation.NONE
+ left <= 0.dp -> CutoutLocation.LEFT
+ right >= getDisplayWidth(context) -> CutoutLocation.RIGHT
+ else -> CutoutLocation.CENTER
+ }
+ DisplayCutout(
+ left,
+ top,
+ right,
+ bottom,
+ location,
+ )
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout())
+
+ // TODO(b/298525212): remove once Compose exposes window inset bounds.
+ private fun getDisplayWidth(context: Context): Dp {
+ val point = Point()
+ checkNotNull(context.display).getRealSize(point)
+ return point.x.dp
+ }
+
+ // TODO(b/298525212): remove once Compose exposes window inset bounds.
+ private fun Int.toDp(context: Context): Dp {
+ return (this.toFloat() / context.resources.displayMetrics.density).dp
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index 4c2c97981702..22645c4532f6 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -25,7 +25,7 @@ import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updateMargins
-import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.compose.ComposeInitializer
import com.android.systemui.res.R
/** A view that can serve as the root of the main SysUI window. */
@@ -45,16 +45,16 @@ open class WindowRootView(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- if (ComposeFacade.isComposeAvailable() && isRoot()) {
- ComposeFacade.composeInitializer().onAttachedToWindow(this)
+ if (isRoot()) {
+ ComposeInitializer.onAttachedToWindow(this)
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
- if (ComposeFacade.isComposeAvailable() && isRoot()) {
- ComposeFacade.composeInitializer().onDetachedFromWindow(this)
+ if (isRoot()) {
+ ComposeInitializer.onDetachedFromWindow(this)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 6af9b739da52..e92630fc67a2 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -55,15 +55,15 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.settings.SecureSettings;
-import java.util.concurrent.Executor;
-
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import java.util.concurrent.Executor;
+
public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
private static final String TAG = "CentralSurfaces.BrightnessController";
- private static final int SLIDER_ANIMATION_DURATION = 3000;
+ private static final int SLIDER_ANIMATION_DURATION = 1000;
private static final int MSG_UPDATE_SLIDER = 1;
private static final int MSG_ATTACH_LISTENER = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index df845f559f2e..d3869baf16a2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -23,11 +23,12 @@ import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.compose.ComposeFacade.createCommunalContainer
-import com.android.systemui.compose.ComposeFacade.isComposeAvailable
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.res.R
@@ -35,7 +36,6 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.kotlin.collectFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
/**
* Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -107,8 +107,7 @@ constructor(
private var shadeShowing = false
/** Returns a flow that tracks whether communal hub is available. */
- fun communalAvailable(): Flow<Boolean> =
- if (isComposeAvailable()) communalInteractor.isCommunalAvailable else flowOf(false)
+ fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable
/**
* Creates the container view containing the glanceable hub UI.
@@ -118,7 +117,11 @@ constructor(
fun initView(
context: Context,
): View {
- return initView(createCommunalContainer(context, communalViewModel))
+ return initView(
+ ComposeView(context).apply {
+ setContent { PlatformTheme { CommunalContainer(viewModel = communalViewModel) } }
+ }
+ )
}
/** Override for testing. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7bcb1da5c2bf..1876f4739c87 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -111,8 +111,6 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.animation.ActivityTransitionAnimator;
-import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
@@ -219,7 +217,6 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
@@ -259,10 +256,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private static final boolean DEBUG_DRAWABLE = false;
/** The parallax amount of the quick settings translation when dragging down the panel. */
public static final float QS_PARALLAX_AMOUNT = 0.175f;
- private static final long ANIMATION_DELAY_ICON_FADE_IN =
- ActivityTransitionAnimator.TIMINGS.getTotalDuration()
- - CollapsedStatusBarFragment.FADE_IN_DURATION
- - CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
private static final int NO_FIXED_DURATION = -1;
private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
@@ -463,7 +456,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
*/
private float mLinearDarkAmount;
private boolean mPulsing;
- private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
/** Non-null if a heads-up notification's position is being tracked. */
@Nullable
@@ -3159,10 +3151,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
- @Override
public boolean shouldHideStatusBarIconsWhenExpanded() {
if (isLaunchingActivity()) {
- return mHideIconsDuringLaunchAnimation;
+ return false;
}
if (mHeadsUpAppearanceController != null
&& mHeadsUpAppearanceController.shouldBeVisible()) {
@@ -3260,18 +3251,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
@Override
- public void applyLaunchAnimationProgress(float linearProgress) {
- boolean hideIcons = TransitionAnimator.getProgress(ActivityTransitionAnimator.TIMINGS,
- linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
- if (hideIcons != mHideIconsDuringLaunchAnimation) {
- mHideIconsDuringLaunchAnimation = hideIcons;
- if (!hideIcons) {
- mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
- }
- }
- }
-
- @Override
public void performHapticFeedback(int constant) {
mVibratorHelper.performHapticFeedback(mView, constant);
}
@@ -3482,7 +3461,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount);
ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount);
ipw.print("mPulsing="); ipw.println(mPulsing);
- ipw.print("mHideIconsDuringLaunchAnimation="); ipw.println(mHideIconsDuringLaunchAnimation);
ipw.print("mStackScrollerMeasuringPass="); ipw.println(mStackScrollerMeasuringPass);
ipw.print("mPanelAlpha="); ipw.println(mPanelAlpha);
ipw.print("mBottomAreaShadeAlpha="); ipw.println(mBottomAreaShadeAlpha);
@@ -4174,7 +4152,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return mShadeRepository.getLegacyIsClosing().getValue();
}
- @Override
public void collapseWithDuration(int animationDuration) {
mFixedDuration = animationDuration;
collapse(false /* delayed */, 1.0f /* speedUpFactor */);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index ec4b23a56483..0a57b64b1ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -78,6 +78,14 @@ public interface ShadeController extends CoreStartable {
*/
void animateCollapseShade(int flags, boolean force, boolean delayed, float speedUpFactor);
+ /**
+ * Collapses the shade with an animation duration in milliseconds.
+ *
+ * @deprecated use animateCollapseShade with a speed up factor instead
+ */
+ @Deprecated
+ void collapseWithDuration(int animationDuration);
+
/** Expand the shade with an animation. */
void animateExpandShade();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt
index 08a0c934702d..093690ffb881 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt
@@ -33,6 +33,7 @@ open class ShadeControllerEmptyImpl @Inject constructor() : ShadeController {
delayed: Boolean,
speedUpFactor: Float
) {}
+ override fun collapseWithDuration(animationDuration: Int) {}
override fun animateExpandShade() {}
override fun animateExpandQs() {}
override fun postAnimateCollapseShade() {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index e6555f2e0993..d99d607879cc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -147,6 +147,11 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
}
@Override
+ public void collapseWithDuration(int animationDuration) {
+ mNpvc.get().collapseWithDuration(animationDuration);
+ }
+
+ @Override
protected void expandToNotifications() {
getNpvc().expandToNotifications();
}
@@ -221,7 +226,6 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
}
}
-
@Override
public void collapseShade(boolean animate) {
if (animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 6a2a6a417f5a..efd9ce0d08b2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -96,7 +96,7 @@ constructor(
}
override fun instantCollapseShade() {
- // TODO(b/315921512) add support for instant transition
+ // TODO(b/325602936) add support for instant transition
sceneInteractor.changeScene(
getCollapseDestinationScene(),
"hide shade",
@@ -133,6 +133,11 @@ constructor(
}
}
+ override fun collapseWithDuration(animationDuration: Int) {
+ // TODO(b/300258424) inline this. The only caller uses the default duration.
+ animateCollapseShade()
+ }
+
private fun animateCollapseShadeInternal() {
sceneInteractor.changeScene(
getCollapseDestinationScene(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 44c6a82d93ca..1f4a0f1f1b91 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -58,9 +58,6 @@ interface ShadeViewController {
/** Collapses the shade. */
fun collapse(animate: Boolean, delayed: Boolean, speedUpFactor: Float)
- /** Collapses the shade with an animation duration in milliseconds. */
- fun collapseWithDuration(animationDuration: Int)
-
/** Collapses the shade instantly without animation. */
fun instantCollapse()
@@ -102,9 +99,6 @@ interface ShadeViewController {
/** Returns the StatusBarState. */
val barState: Int
- /** Sets the amount of progress in the status bar launch animation. */
- fun applyLaunchAnimationProgress(linearProgress: Float)
-
/** Sets the alpha value of the shade to a value between 0 and 255. */
fun setAlpha(alpha: Int, animate: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 7a181f106514..d756f026e21d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -37,7 +37,6 @@ class ShadeViewControllerEmptyImpl @Inject constructor() :
override val isShadeFullyExpanded: Boolean = false
override fun collapse(delayed: Boolean, speedUpFactor: Float) {}
override fun collapse(animate: Boolean, delayed: Boolean, speedUpFactor: Float) {}
- override fun collapseWithDuration(animationDuration: Int) {}
override fun instantCollapse() {}
override fun animateCollapseQs(fullyCollapse: Boolean) {}
override fun canBeCollapsed(): Boolean = false
@@ -55,7 +54,6 @@ class ShadeViewControllerEmptyImpl @Inject constructor() :
override fun dozeTimeTick() {}
override fun resetViews(animate: Boolean) {}
override val barState: Int = 0
- override fun applyLaunchAnimationProgress(linearProgress: Float) {}
override fun closeUserSwitcherIfOpen(): Boolean {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
index 3a0f03f70e1c..f68141a4181c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -19,11 +19,13 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -35,10 +37,11 @@ import kotlinx.coroutines.flow.distinctUntilChanged
class NotificationStackAppearanceViewModel
@Inject
constructor(
+ dumpManager: DumpManager,
stackAppearanceInteractor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
sceneInteractor: SceneInteractor,
-) {
+) : FlowDumperImpl(dumpManager) {
/**
* The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
* from Gone to Shade scenes, and remain at 1 when in Lockscreen or Shade scenes and while
@@ -72,10 +75,12 @@ constructor(
}
}
.distinctUntilChanged()
+ .dumpWhileCollecting("expandFraction")
/** The bounds of the notification stack in the current scene. */
- val stackBounds: Flow<NotificationContainerBounds> = stackAppearanceInteractor.stackBounds
+ val stackBounds: Flow<NotificationContainerBounds> =
+ stackAppearanceInteractor.stackBounds.dumpValue("stackBounds")
/** The y-coordinate in px of top of the contents of the notification stack. */
- val contentTop: StateFlow<Float> = stackAppearanceInteractor.contentTop
+ val contentTop: StateFlow<Float> = stackAppearanceInteractor.contentTop.dumpValue("contentTop")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index b4c88c5f9e79..f5237938ebfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -60,6 +61,7 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTran
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -88,6 +90,7 @@ class SharedNotificationContainerViewModel
@Inject
constructor(
private val interactor: SharedNotificationContainerInteractor,
+ dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -116,7 +119,7 @@ constructor(
private val primaryBouncerToLockscreenTransitionViewModel:
PrimaryBouncerToLockscreenTransitionViewModel,
private val aodBurnInViewModel: AodBurnInViewModel,
-) {
+) : FlowDumperImpl(dumpManager) {
private val statesForConstrainedNotifications: Set<KeyguardState> =
setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER)
@@ -126,6 +129,7 @@ constructor(
.map { it.transitionState == STARTED || it.transitionState == RUNNING }
.distinctUntilChanged()
.onStart { emit(false) }
+ .dumpWhileCollecting("lockscreenToGlanceableHubRunning")
private val glanceableHubToLockscreenRunning =
keyguardTransitionInteractor
@@ -133,6 +137,7 @@ constructor(
.map { it.transitionState == STARTED || it.transitionState == RUNNING }
.distinctUntilChanged()
.onStart { emit(false) }
+ .dumpWhileCollecting("glanceableHubToLockscreenRunning")
/**
* Shade locked is a legacy concept, but necessary to mimic current functionality. Listen for
@@ -148,8 +153,10 @@ constructor(
isShadeLocked && (isQsExpanded || isShadeExpanded)
}
.distinctUntilChanged()
+ .dumpWhileCollecting("isShadeLocked")
- val shadeCollapseFadeInComplete = MutableStateFlow(false)
+ private val shadeCollapseFadeInComplete = MutableStateFlow(false)
+ .dumpValue("shadeCollapseFadeInComplete")
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
interactor.configurationBasedDimensions
@@ -171,6 +178,7 @@ constructor(
)
}
.distinctUntilChanged()
+ .dumpWhileCollecting("configurationBasedDimensions")
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
@@ -186,6 +194,7 @@ constructor(
constrainedNotificationState || transitioningToOrFromLockscreen
}
.distinctUntilChanged()
+ .dumpWhileCollecting("isOnLockscreen")
/** Are we purely on the keyguard without the shade/qs? */
val isOnLockscreenWithoutShade: Flow<Boolean> =
@@ -204,6 +213,7 @@ constructor(
started = SharingStarted.Eagerly,
initialValue = false,
)
+ .dumpValue("isOnLockscreenWithoutShade")
/** Are we purely on the glanceable hub without the shade/qs? */
val isOnGlanceableHubWithoutShade: Flow<Boolean> =
@@ -222,6 +232,7 @@ constructor(
started = SharingStarted.WhileSubscribed(),
initialValue = false,
)
+ .dumpWhileCollecting("isOnGlanceableHubWithoutShade")
/**
* Fade in if the user swipes the shade back up, not if collapsed by going to AOD. This is
@@ -284,6 +295,7 @@ constructor(
started = SharingStarted.WhileSubscribed(),
initialValue = false,
)
+ .dumpWhileCollecting("shadeCollapseFadeIn")
/**
* The container occupies the entire screen, and must be positioned relative to other elements.
@@ -322,6 +334,7 @@ constructor(
started = SharingStarted.Lazily,
initialValue = NotificationContainerBounds(),
)
+ .dumpValue("bounds")
/**
* Ensure view is visible when the shade/qs are expanded. Also, as QS is expanding, fade out
@@ -345,6 +358,7 @@ constructor(
}
}
.onStart { emit(0f) }
+ .dumpWhileCollecting("alphaForShadeAndQsExpansion")
private val alphaWhenGoneAndShadeState: Flow<Float> =
combineTransform(
@@ -357,6 +371,7 @@ constructor(
emit(1f)
}
}
+ .dumpWhileCollecting("alphaWhenGoneAndShadeState")
fun expansionAlpha(viewState: ViewStateAccessor): Flow<Float> {
// All transition view models are mututally exclusive, and safe to merge
@@ -389,7 +404,9 @@ constructor(
isOnLockscreenWithoutShade,
shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
- keyguardInteractor.dismissAlpha,
+ keyguardInteractor.dismissAlpha.dumpWhileCollecting(
+ "keyguardInteractor.keyguardAlpha"
+ ),
) {
isOnLockscreenWithoutShade,
shadeCollapseFadeIn,
@@ -405,6 +422,7 @@ constructor(
},
)
.distinctUntilChanged()
+ .dumpWhileCollecting("expansionAlpha")
}
/**
@@ -438,6 +456,7 @@ constructor(
}
}
}
+ .dumpWhileCollecting("glanceableHubAlpha")
/**
* Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
@@ -458,6 +477,7 @@ constructor(
0f
}
}
+ .dumpWhileCollecting("translationY")
}
/**
@@ -469,6 +489,7 @@ constructor(
lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX,
glanceableHubToLockscreenTransitionViewModel.notificationTranslationX,
)
+ .dumpWhileCollecting("translationX")
/**
* When on keyguard, there is limited space to display notifications so calculate how many could
@@ -510,6 +531,7 @@ constructor(
}
}
.distinctUntilChanged()
+ .dumpWhileCollecting("maxNotifications")
}
fun notificationStackChanged() {
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 270b94b1893e..23a080b7b931 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -45,8 +45,8 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -71,7 +71,7 @@ constructor(
private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val shadeControllerLazy: Lazy<ShadeController>,
- private val shadeViewControllerLazy: Lazy<ShadeViewController>,
+ private val commandQueue: CommandQueue,
private val shadeAnimationInteractor: ShadeAnimationInteractor,
private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
@@ -853,10 +853,11 @@ constructor(
if (dismissShade) {
return StatusBarTransitionAnimatorController(
animationController,
- shadeViewControllerLazy.get(),
shadeAnimationInteractor,
shadeControllerLazy.get(),
notifShadeWindowControllerLazy.get(),
+ commandQueue,
+ displayId,
isLaunchForActivity
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 5610ed926f70..b5ab4e3eb462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -63,6 +63,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -105,6 +106,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final NotificationVisibilityProvider mVisibilityProvider;
private final HeadsUpManager mHeadsUpManager;
private final ActivityStarter mActivityStarter;
+ private final CommandQueue mCommandQueue;
private final NotificationClickNotifier mClickNotifier;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
@@ -143,6 +145,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
NotificationVisibilityProvider visibilityProvider,
HeadsUpManager headsUpManager,
ActivityStarter activityStarter,
+ CommandQueue commandQueue,
NotificationClickNotifier clickNotifier,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
@@ -175,6 +178,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mActivityStarter = activityStarter;
+ mCommandQueue = commandQueue;
mClickNotifier = clickNotifier;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
@@ -444,10 +448,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
ActivityTransitionAnimator.Controller animationController =
new StatusBarTransitionAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row, null),
- mShadeViewController,
mShadeAnimationInteractor,
mShadeController,
mNotificationShadeWindowController,
+ mCommandQueue,
+ mDisplayId,
isActivityIntent);
mActivityTransitionAnimator.startPendingIntentWithAnimation(
animationController,
@@ -486,10 +491,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
ActivityTransitionAnimator.Controller animationController =
new StatusBarTransitionAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row),
- mShadeViewController,
mShadeAnimationInteractor,
mShadeController,
mNotificationShadeWindowController,
+ mCommandQueue,
+ mDisplayId,
true /* isActivityIntent */);
mActivityTransitionAnimator.startIntentWithAnimation(
@@ -537,10 +543,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
viewController == null ? null
: new StatusBarTransitionAnimatorController(
viewController,
- mShadeViewController,
mShadeAnimationInteractor,
mShadeController,
mNotificationShadeWindowController,
+ mCommandQueue,
+ mDisplayId,
true /* isActivityIntent */);
mActivityTransitionAnimator.startIntentWithAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
index 7e907d80d277..705a11df83fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTransitionAnimatorController.kt
@@ -3,10 +3,13 @@ package com.android.systemui.statusbar.phone
import android.view.View
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
+import com.android.systemui.animation.TransitionAnimator.Companion.getProgress
+import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.shade.ShadeController
-import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
/**
* A [ActivityTransitionAnimator.Controller] that takes care of collapsing the status bar at the
@@ -14,12 +17,15 @@ import com.android.systemui.statusbar.NotificationShadeWindowController
*/
class StatusBarTransitionAnimatorController(
private val delegate: ActivityTransitionAnimator.Controller,
- private val shadeViewController: ShadeViewController,
private val shadeAnimationInteractor: ShadeAnimationInteractor,
private val shadeController: ShadeController,
private val notificationShadeWindowController: NotificationShadeWindowController,
+ private val commandQueue: CommandQueue,
+ @DisplayId private val displayId: Int,
private val isLaunchForActivity: Boolean = true
) : ActivityTransitionAnimator.Controller by delegate {
+ private var hideIconsDuringLaunchAnimation: Boolean = true
+
// Always sync the opening window with the shade, given that we draw a hole punch in the shade
// of the same size and position as the opening app to make it visible.
override val openingWindowSyncView: View?
@@ -38,7 +44,7 @@ class StatusBarTransitionAnimatorController(
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
shadeAnimationInteractor.setIsLaunchingActivity(true)
if (!isExpandingFullyAbove) {
- shadeViewController.collapseWithDuration(
+ shadeController.collapseWithDuration(
ActivityTransitionAnimator.TIMINGS.totalDuration.toInt()
)
}
@@ -56,7 +62,19 @@ class StatusBarTransitionAnimatorController(
linearProgress: Float
) {
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
- shadeViewController.applyLaunchAnimationProgress(linearProgress)
+ val hideIcons =
+ getProgress(
+ ActivityTransitionAnimator.TIMINGS,
+ linearProgress,
+ ANIMATION_DELAY_ICON_FADE_IN,
+ 100
+ ) == 0.0f
+ if (hideIcons != hideIconsDuringLaunchAnimation) {
+ hideIconsDuringLaunchAnimation = hideIcons
+ if (!hideIcons) {
+ commandQueue.recomputeDisableFlags(displayId, true /* animate */)
+ }
+ }
}
override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
@@ -64,4 +82,12 @@ class StatusBarTransitionAnimatorController(
shadeAnimationInteractor.setIsLaunchingActivity(false)
shadeController.onLaunchAnimationCancelled(isLaunchForActivity)
}
+
+ companion object {
+ val ANIMATION_DELAY_ICON_FADE_IN =
+ (ActivityTransitionAnimator.TIMINGS.totalDuration -
+ CollapsedStatusBarFragment.FADE_IN_DURATION -
+ CollapsedStatusBarFragment.FADE_IN_DELAY -
+ 48)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt
index d90a9c75deec..485f4b5cbfd7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt
@@ -17,23 +17,18 @@
package com.android.systemui.volume.panel.shared.flag
import com.android.systemui.Flags
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.flags.RefactorFlagUtils
import javax.inject.Inject
/** Provides a flag to check for the new Compose based Volume Panel availability. */
class VolumePanelFlag @Inject constructor() {
- /**
- * Returns true when the new Volume Panel is available and false the otherwise. The new panel
- * can only be available when [ComposeFacade.isComposeAvailable] is true.
- */
+ /** Returns true when the new Volume Panel is available and false the otherwise. */
fun canUseNewVolumePanel(): Boolean {
- return ComposeFacade.isComposeAvailable() && Flags.newVolumePanel()
+ return Flags.newVolumePanel()
}
fun assertNewVolumePanel() {
- require(ComposeFacade.isComposeAvailable())
RefactorFlagUtils.assertInNewMode(Flags.newVolumePanel(), Flags.FLAG_NEW_VOLUME_PANEL)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
index 53e1b8b5bb70..d430e65770fd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
@@ -18,11 +18,12 @@ package com.android.systemui.volume.panel.ui.activity
import android.os.Bundle
import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag
+import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
import javax.inject.Provider
@@ -44,7 +45,7 @@ constructor(
volumePanelFlag.assertNewVolumePanel()
- ComposeFacade.setVolumePanelActivityContent(this, viewModel) { finish() }
+ setContent { VolumePanelRoot(viewModel = viewModel, onDismiss = ::finish) }
}
override fun onContentChanged() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
index 3e6cc3bb4f6b..03e4f9ae1685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
@@ -32,10 +32,6 @@ import org.junit.runner.RunWith
class ComposeInitializerTest : SysuiTestCase() {
@Test
fun testCanAddComposeViewInInitializedWindow() {
- if (!ComposeFacade.isComposeAvailable()) {
- return
- }
-
val root = TestWindowRoot(context)
try {
runOnMainThreadAndWaitForIdleSync { ViewUtils.attachView(root) }
@@ -55,12 +51,12 @@ class ComposeInitializerTest : SysuiTestCase() {
class TestWindowRoot(context: Context) : FrameLayout(context) {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- ComposeFacade.composeInitializer().onAttachedToWindow(this)
+ ComposeInitializer.onAttachedToWindow(this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
- ComposeFacade.composeInitializer().onDetachedFromWindow(this)
+ ComposeInitializer.onDetachedFromWindow(this)
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index a992956f5121..59d8fc3d3fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -19,7 +19,6 @@ package com.android.systemui.keyboard.stickykeys.ui
import android.app.Dialog
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.keyboard.data.repository.FakeStickyKeysRepository
import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
@@ -34,7 +33,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
-import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,19 +52,22 @@ class StickyKeysIndicatorCoordinatorTest : SysuiTestCase() {
@Before
fun setup() {
- Assume.assumeTrue(ComposeFacade.isComposeAvailable())
val dialogFactory = mock<StickyKeyDialogFactory>()
whenever(dialogFactory.create(any())).thenReturn(dialog)
val keyboardRepository = Kosmos().keyboardRepository
- val viewModel = StickyKeysIndicatorViewModel(
+ val viewModel =
+ StickyKeysIndicatorViewModel(
stickyKeysRepository,
keyboardRepository,
- testScope.backgroundScope)
- coordinator = StickyKeysIndicatorCoordinator(
+ testScope.backgroundScope
+ )
+ coordinator =
+ StickyKeysIndicatorCoordinator(
testScope.backgroundScope,
dialogFactory,
viewModel,
- mock<StickyKeysLogger>())
+ mock<StickyKeysLogger>()
+ )
coordinator.startListening()
keyboardRepository.setIsAnyKeyboardConnected(true)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 69cd173f4253..72a890d6a5e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -169,6 +169,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
+ powerInteractor = powerInteractor,
)
.apply { start() }
@@ -574,8 +575,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runCurrent()
// WHEN the device begins to wake
+ keyguardRepository.setKeyguardShowing(true)
powerInteractor.setAwakeForTest()
- runCurrent()
+ advanceTimeBy(60L)
assertThat(transitionRepository)
.startedTransition(
@@ -630,15 +632,42 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
- fun dozingToGone() =
+ fun dozingToGoneWithUnlock() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
+ runCurrent()
// WHEN biometrics succeeds with wake and unlock mode
+ powerInteractor.setAwakeForTest()
keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ advanceTimeBy(60L)
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GONE,
+ from = KeyguardState.DOZING,
+ ownerName = "FromDozingTransitionInteractor",
+ animatorAssertion = { it.isNotNull() }
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ /** This handles security method NONE and screen off with lock timeout */
+ @Test
+ fun dozingToGoneWithKeyguardNotShowing() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DOZING
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
runCurrent()
+ // WHEN the device wakes up without a keyguard
+ keyguardRepository.setKeyguardShowing(false)
+ keyguardRepository.setKeyguardDismissible(true)
+ powerInteractor.setAwakeForTest()
+ advanceTimeBy(60L)
+
assertThat(transitionRepository)
.startedTransition(
to = KeyguardState.GONE,
@@ -666,8 +695,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runCurrent()
// WHEN the device begins to wake
+ keyguardRepository.setKeyguardShowing(true)
powerInteractor.setAwakeForTest()
- runCurrent()
+ advanceTimeBy(60L)
assertThat(transitionRepository)
.startedTransition(
@@ -1335,8 +1365,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// WHEN the keyguard is occluded and device wakes up
keyguardRepository.setKeyguardOccluded(true)
+ keyguardRepository.setKeyguardShowing(true)
powerInteractor.setAwakeForTest()
- runCurrent()
+ advanceTimeBy(60L)
// THEN a transition to OCCLUDED should occur
assertThat(transitionRepository)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
index 83932b0a6133..dbfab64b004a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
@@ -49,6 +49,19 @@ class ActivityTaskManagerTasksRepositoryTest : SysuiTestCase() {
)
@Test
+ fun launchRecentTask_taskIsMovedToForeground() =
+ testScope.runTest {
+ val currentForegroundTask by collectLastValue(repo.foregroundTask)
+ val newForegroundTask = createTask(taskId = 1)
+ val backgroundTask = createTask(taskId = 2)
+ fakeActivityTaskManager.addRunningTasks(backgroundTask, newForegroundTask)
+
+ repo.launchRecentTask(newForegroundTask)
+
+ assertThat(currentForegroundTask).isEqualTo(newForegroundTask)
+ }
+
+ @Test
fun findRunningTaskFromWindowContainerToken_noMatch_returnsNull() {
fakeActivityTaskManager.addRunningTasks(createTask(taskId = 1), createTask(taskId = 2))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt
index 1c4870bc32b1..920e5ee94cca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeActivityTaskManager.kt
@@ -17,7 +17,7 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
import android.app.ActivityManager.RunningTaskInfo
-import android.app.ActivityTaskManager
+import android.app.IActivityTaskManager
import android.app.TaskStackListener
import android.content.Intent
import android.window.IWindowContainerToken
@@ -31,7 +31,7 @@ class FakeActivityTaskManager {
private val runningTasks = mutableListOf<RunningTaskInfo>()
private val taskTaskListeners = mutableListOf<TaskStackListener>()
- val activityTaskManager = mock<ActivityTaskManager>()
+ val activityTaskManager = mock<IActivityTaskManager>()
init {
whenever(activityTaskManager.registerTaskStackListener(any())).thenAnswer {
@@ -42,10 +42,20 @@ class FakeActivityTaskManager {
taskTaskListeners -= it.arguments[0] as TaskStackListener
return@thenAnswer Unit
}
- whenever(activityTaskManager.getTasks(any())).thenAnswer {
+ whenever(activityTaskManager.getTasks(any(), any(), any(), any())).thenAnswer {
val maxNumTasks = it.arguments[0] as Int
return@thenAnswer runningTasks.take(maxNumTasks)
}
+ whenever(activityTaskManager.startActivityFromRecents(any(), any())).thenAnswer {
+ val taskId = it.arguments[0] as Int
+ val runningTask = runningTasks.find { runningTask -> runningTask.taskId == taskId }
+ if (runningTask != null) {
+ moveTaskToForeground(runningTask)
+ return@thenAnswer 0
+ } else {
+ return@thenAnswer -1
+ }
+ }
}
fun moveTaskToForeground(task: RunningTaskInfo) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt
index 44c411fdb1d1..28393e837b93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/FakeMediaProjectionManager.kt
@@ -22,6 +22,8 @@ import android.os.Binder
import android.os.IBinder
import android.os.UserHandle
import android.view.ContentRecordingSession
+import android.window.WindowContainerToken
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -29,6 +31,7 @@ import com.android.systemui.util.mockito.whenever
class FakeMediaProjectionManager {
val mediaProjectionManager = mock<MediaProjectionManager>()
+ val helper = mock<MediaProjectionServiceHelper>()
private val callbacks = mutableListOf<MediaProjectionManager.Callback>()
@@ -41,6 +44,11 @@ class FakeMediaProjectionManager {
callbacks -= it.arguments[0] as MediaProjectionManager.Callback
return@thenAnswer Unit
}
+ whenever(helper.updateTaskRecordingSession(any())).thenAnswer {
+ val token = it.arguments[0] as WindowContainerToken
+ dispatchOnSessionSet(session = createSingleTaskSession(token.asBinder()))
+ return@thenAnswer true
+ }
}
fun dispatchOnStart(info: MediaProjectionInfo = DEFAULT_INFO) {
@@ -61,6 +69,7 @@ class FakeMediaProjectionManager {
companion object {
fun createDisplaySession(): ContentRecordingSession =
ContentRecordingSession.createDisplaySession(/* displayToMirror = */ 123)
+
fun createSingleTaskSession(token: IBinder = Binder()): ContentRecordingSession =
ContentRecordingSession.createTaskSession(token)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
index 7bd97ce2670c..fdd434acdc9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -28,9 +28,8 @@ import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeAct
import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,7 +39,7 @@ import org.junit.runner.RunWith
@SmallTest
class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
- private val dispatcher = StandardTestDispatcher()
+ private val dispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(dispatcher)
private val fakeMediaProjectionManager = FakeMediaProjectionManager()
@@ -58,14 +57,27 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
handler = Handler.getMain(),
applicationScope = testScope.backgroundScope,
- tasksRepository = tasksRepo
+ tasksRepository = tasksRepo,
+ backgroundDispatcher = dispatcher,
+ mediaProjectionServiceHelper = fakeMediaProjectionManager.helper
)
@Test
+ fun switchProjectedTask_stateIsUpdatedWithNewTask() =
+ testScope.runTest {
+ val task = createTask(taskId = 1)
+ val state by collectLastValue(repo.mediaProjectionState)
+
+ fakeActivityTaskManager.addRunningTasks(task)
+ repo.switchProjectedTask(task)
+
+ assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task))
+ }
+
+ @Test
fun mediaProjectionState_onStart_emitsNotProjecting() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnStart()
@@ -76,7 +88,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_onStop_emitsNotProjecting() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnStop()
@@ -87,7 +98,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_onSessionSet_sessionNull_emitsNotProjecting() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnSessionSet(session = null)
@@ -98,7 +108,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_onSessionSet_contentToRecordDisplay_emitsEntireScreen() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnSessionSet(
session = ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123)
@@ -111,7 +120,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
fun mediaProjectionState_sessionSet_taskWithToken_noMatchingRunningTask_emitsEntireScreen() =
testScope.runTest {
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
val taskWindowContainerToken = Binder()
fakeMediaProjectionManager.dispatchOnSessionSet(
@@ -128,7 +136,6 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
val task = createTask(taskId = 1, token = token)
fakeActivityTaskManager.addRunningTasks(task)
val state by collectLastValue(repo.mediaProjectionState)
- runCurrent()
fakeMediaProjectionManager.dispatchOnSessionSet(
session = ContentRecordingSession.createTaskSession(token.asBinder())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
index b2ebe1bcbc8b..dfb688bbde4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
@@ -61,6 +61,8 @@ class TaskSwitchInteractorTest : SysuiTestCase() {
handler = Handler.getMain(),
applicationScope = testScope.backgroundScope,
tasksRepository = tasksRepo,
+ backgroundDispatcher = dispatcher,
+ mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
)
private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo)
@@ -118,6 +120,40 @@ class TaskSwitchInteractorTest : SysuiTestCase() {
}
@Test
+ fun taskSwitchChanges_projectingTask_foregroundTaskDifferent_thenSwitched_emitsUnchanged() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 0)
+ val foregroundTask = createTask(taskId = 1)
+ val taskSwitchState by collectLastValue(interactor.taskSwitchChanges)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(token = projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ interactor.switchProjectedTask(foregroundTask)
+
+ assertThat(taskSwitchState).isEqualTo(TaskSwitchState.TaskUnchanged)
+ }
+
+ @Test
+ fun taskSwitchChanges_projectingTask_foregroundTaskDifferent_thenWentBack_emitsUnchanged() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 0)
+ val foregroundTask = createTask(taskId = 1)
+ val taskSwitchState by collectLastValue(interactor.taskSwitchChanges)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(token = projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ interactor.goBackToTask(projectedTask)
+
+ assertThat(taskSwitchState).isEqualTo(TaskSwitchState.TaskUnchanged)
+ }
+
+ @Test
fun taskSwitchChanges_projectingTask_foregroundTaskLauncher_emitsTaskUnchanged() =
testScope.runTest {
val projectedTask = createTask(taskId = 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
index d0c6d7cddc46..c4e939339fa1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
@@ -21,14 +21,15 @@ import android.app.NotificationManager
import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager
+import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionManager
import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionManagerRepository
import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
@@ -42,6 +43,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@OptIn(ExperimentalCoroutinesApi::class)
@@ -49,7 +51,7 @@ import org.mockito.Mockito.verify
@SmallTest
class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
- private val notificationManager: NotificationManager = mock()
+ private val notificationManager = mock<NotificationManager>()
private val dispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(dispatcher)
@@ -70,22 +72,26 @@ class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
handler = Handler.getMain(),
applicationScope = testScope.backgroundScope,
tasksRepository = tasksRepo,
+ backgroundDispatcher = dispatcher,
+ mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
)
private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo)
- private val viewModel = TaskSwitcherNotificationViewModel(interactor)
-
- private val coordinator =
- TaskSwitcherNotificationCoordinator(
- context,
- notificationManager,
- testScope.backgroundScope,
- dispatcher,
- viewModel
- )
+ private val viewModel =
+ TaskSwitcherNotificationViewModel(interactor, backgroundDispatcher = dispatcher)
+
+ private lateinit var coordinator: TaskSwitcherNotificationCoordinator
@Before
fun setup() {
+ coordinator =
+ TaskSwitcherNotificationCoordinator(
+ context,
+ notificationManager,
+ testScope.backgroundScope,
+ viewModel,
+ fakeBroadcastDispatcher,
+ )
coordinator.start()
}
@@ -105,7 +111,7 @@ class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
testScope.runTest {
fakeMediaProjectionManager.dispatchOnStop()
- verify(notificationManager).cancel(any())
+ verify(notificationManager).cancel(any(), any())
}
}
@@ -114,7 +120,7 @@ class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
testScope.runTest {
fakeMediaProjectionManager.dispatchOnStop()
val idCancel = argumentCaptor<Int>()
- verify(notificationManager).cancel(idCancel.capture())
+ verify(notificationManager).cancel(any(), idCancel.capture())
switchTask()
val idNotify = argumentCaptor<Int>()
@@ -124,9 +130,55 @@ class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
}
}
+ @Test
+ fun switchTaskAction_hidesNotification() =
+ testScope.runTest {
+ switchTask()
+ val notification = argumentCaptor<Notification>()
+ verify(notificationManager).notify(any(), any(), notification.capture())
+ verify(notificationManager, never()).cancel(any(), any())
+
+ val action = findSwitchAction(notification.value)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ action.actionIntent.intent
+ )
+
+ verify(notificationManager).cancel(any(), any())
+ }
+
+ @Test
+ fun goBackAction_hidesNotification() =
+ testScope.runTest {
+ switchTask()
+ val notification = argumentCaptor<Notification>()
+ verify(notificationManager).notify(any(), any(), notification.capture())
+ verify(notificationManager, never()).cancel(any(), any())
+
+ val action = findGoBackAction(notification.value)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ action.actionIntent.intent
+ )
+
+ verify(notificationManager).cancel(any(), any())
+ }
+
+ private fun findSwitchAction(notification: Notification): Notification.Action {
+ return notification.actions.first {
+ it.title == context.getString(R.string.media_projection_task_switcher_action_switch)
+ }
+ }
+
+ private fun findGoBackAction(notification: Notification): Notification.Action {
+ return notification.actions.first {
+ it.title == context.getString(R.string.media_projection_task_switcher_action_back)
+ }
+ }
+
private fun switchTask() {
- val projectedTask = FakeActivityTaskManager.createTask(taskId = 1)
- val foregroundTask = FakeActivityTaskManager.createTask(taskId = 2)
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
fakeMediaProjectionManager.dispatchOnSessionSet(
session =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
index 7d38de4bc2d3..5dadf21a46b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
@@ -63,11 +63,14 @@ class TaskSwitcherNotificationViewModelTest : SysuiTestCase() {
handler = Handler.getMain(),
applicationScope = testScope.backgroundScope,
tasksRepository = tasksRepo,
+ backgroundDispatcher = dispatcher,
+ mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
)
private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo)
- private val viewModel = TaskSwitcherNotificationViewModel(interactor)
+ private val viewModel =
+ TaskSwitcherNotificationViewModel(interactor, backgroundDispatcher = dispatcher)
@Test
fun uiState_notProjecting_emitsNotShowing() =
@@ -135,6 +138,40 @@ class TaskSwitcherNotificationViewModelTest : SysuiTestCase() {
}
@Test
+ fun uiState_projectingTask_foregroundTaskChanged_thenTaskSwitched_emitsNotShowing() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
+ val uiState by collectLastValue(viewModel.uiState)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ viewModel.onSwitchTaskClicked(foregroundTask)
+
+ assertThat(uiState).isEqualTo(TaskSwitcherNotificationUiState.NotShowing)
+ }
+
+ @Test
+ fun uiState_projectingTask_foregroundTaskChanged_thenGoBack_emitsNotShowing() =
+ testScope.runTest {
+ val projectedTask = createTask(taskId = 1)
+ val foregroundTask = createTask(taskId = 2)
+ val uiState by collectLastValue(viewModel.uiState)
+
+ fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask)
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ session = createSingleTaskSession(projectedTask.token.asBinder())
+ )
+ fakeActivityTaskManager.moveTaskToForeground(foregroundTask)
+ viewModel.onGoBackToTaskClicked(projectedTask)
+
+ assertThat(uiState).isEqualTo(TaskSwitcherNotificationUiState.NotShowing)
+ }
+
+ @Test
fun uiState_projectingTask_foregroundTaskChanged_same_emitsNotShowing() =
testScope.runTest {
val projectedTask = createTask(taskId = 1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 665fc750c742..96574e245d2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -35,7 +35,6 @@ import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
@@ -52,9 +51,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.After
import org.junit.Assert.assertThrows
-import org.junit.Assume.assumeTrue
import org.junit.Before
-import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -305,13 +302,5 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, CONTAINER_HEIGHT.toFloat(), 0)
private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
private val UP_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-
- @BeforeClass
- @JvmStatic
- fun beforeClass() {
- // Glanceable hub requires Compose, no point running any of these tests if compose isn't
- // enabled.
- assumeTrue(ComposeFacade.isComposeAvailable())
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index b426d1de0b00..960fd59b4f10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import org.mockito.Mockito.`when` as whenever
import android.content.Context
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -33,7 +34,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.binder.BouncerViewBinder
import com.android.systemui.classifier.FalsingCollectorFake
-import com.android.systemui.compose.ComposeFacade.isComposeAvailable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlagsClassic
@@ -88,7 +88,6 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -511,10 +510,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Test
@Ignore("b/321332798")
fun setsUpCommunalHubLayout_whenFlagEnabled() {
- if (!isComposeAvailable()) {
- return
- }
-
whenever(mGlanceableHubContainerController.communalAvailable())
.thenReturn(MutableStateFlow(true))
@@ -537,10 +532,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Test
fun doesNotSetupCommunalHubLayout_whenFlagDisabled() {
- if (!isComposeAvailable()) {
- return
- }
-
whenever(mGlanceableHubContainerController.communalAvailable())
.thenReturn(MutableStateFlow(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 3792d5c1d6b9..eb890c7a78d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -234,19 +234,14 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- fun shadeIsExpandedOnStatusIconClick() {
+ fun shadeIsExpandedOnStatusIconMouseClick() {
val view = createViewMock()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
controller = createAndInitController(view)
}
val statusContainer = view.requireViewById<View>(R.id.system_icons)
statusContainer.dispatchTouchEvent(
- getMotionEventFromSource(
- MotionEvent.ACTION_UP,
- 0,
- 0,
- InputDevice.SOURCE_MOUSE
- )
+ getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
)
verify(shadeViewController).expand(any())
}
@@ -259,18 +254,13 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
val statusContainer = view.requireViewById<View>(R.id.system_icons)
val handled = statusContainer.dispatchTouchEvent(
- getMotionEventFromSource(
- MotionEvent.ACTION_UP,
- 0,
- 0,
- InputDevice.SOURCE_TOUCHSCREEN
- )
+ getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
)
assertThat(handled).isFalse()
}
- private fun getMotionEventFromSource(action: Int, x: Int, y: Int, source: Int): MotionEvent {
- val ev = MotionEvent.obtain(0, 0, action, x.toFloat(), y.toFloat(), 0)
+ private fun getActionUpEventFromSource(source: Int): MotionEvent {
+ val ev = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0f, 0f, 0)
ev.source = source
return ev
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 41514ce3e72c..938b2f88d4b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -79,6 +79,7 @@ import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeAnimationRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -130,6 +131,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private ActivityStarter mActivityStarter;
@Mock
+ private CommandQueue mCommandQueue;
+ @Mock
private NotificationClickNotifier mClickNotifier;
@Mock
private StatusBarStateController mStatusBarStateController;
@@ -236,6 +239,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mVisibilityProvider,
headsUpManager,
mActivityStarter,
+ mCommandQueue,
mClickNotifier,
mStatusBarKeyguardViewManager,
mock(KeyguardManager.class),
@@ -257,7 +261,9 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(NotificationShadeWindowController.class),
mActivityTransitionAnimator,
new ShadeAnimationInteractorLegacyImpl(
- new ShadeAnimationRepository(), new FakeShadeRepository()),
+ new ShadeAnimationRepository(),
+ new FakeShadeRepository()
+ ),
notificationAnimationProvider,
mock(LaunchFullScreenIntentProvider.class),
mPowerInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
index 3faa6eb8f5f2..4e05de27bb33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
@@ -17,7 +17,6 @@
package com.android.systemui.flags
import android.util.Log
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import org.junit.Assert
import org.junit.Assume
@@ -42,10 +41,6 @@ class SceneContainerRule : TestRule {
null || description?.getAnnotation(EnableSceneContainer::class.java) != null
if (hasAnnotation) {
Assume.assumeTrue(
- "Compose must be available for @EnableSceneContainer test",
- ComposeFacade.isComposeAvailable()
- )
- Assume.assumeTrue(
"Couldn't set Flags.SCENE_CONTAINER_ENABLED for @EnableSceneContainer test",
trySetSceneContainerEnabled(true)
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index da5cd679351f..2477415cc06e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepos
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.powerInteractor
val Kosmos.fromAodTransitionInteractor by
Kosmos.Fixture {
@@ -30,5 +31,6 @@ val Kosmos.fromAodTransitionInteractor by
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
+ powerInteractor = powerInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 8ca53e6591c0..d5d357f81b35 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -39,7 +39,9 @@ val Kosmos.keyguardRootViewModel by Fixture {
keyguardTransitionInteractor = keyguardTransitionInteractor,
notificationsKeyguardInteractor = notificationsKeyguardInteractor,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
+ aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+ dozingToGoneTransitionViewModel = dozingToGoneTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
index d79633ae72ba..bada2a61995d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -24,6 +25,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.notif
val Kosmos.notificationStackAppearanceViewModel by Fixture {
NotificationStackAppearanceViewModel(
+ dumpManager = dumpManager,
stackAppearanceInteractor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
sceneInteractor = sceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 832344d7a822..106e85cc8d85 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToGoneTransitionViewModel
@@ -48,6 +49,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.sharedNotificationContainerViewModel by Fixture {
SharedNotificationContainerViewModel(
interactor = sharedNotificationContainerInteractor,
+ dumpManager = dumpManager,
applicationScope = applicationCoroutineScope,
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
index 41c11ad61c7f..7a86c4f73a3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
@@ -33,6 +33,7 @@ import com.android.systemui.settings.userTracker
import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor
import com.android.systemui.shade.shadeController
import com.android.systemui.shade.shadeViewController
+import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.notification.collection.provider.launchFullScreenIntentProvider
import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider
import com.android.systemui.statusbar.notification.notificationTransitionAnimatorControllerProvider
@@ -59,6 +60,7 @@ val Kosmos.statusBarNotificationActivityStarter by
notificationVisibilityProvider,
headsUpManager,
activityStarter,
+ commandQueue,
notificationClickNotifier,
statusBarKeyguardViewManager,
keyguardManager,
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 6ca60be25023..f31eb44f23f5 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -39,9 +39,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
-import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -51,22 +49,16 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
-import android.view.Display;
-import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -110,9 +102,6 @@ public class WallpaperBackupAgent extends BackupAgent {
@VisibleForTesting
static final String WALLPAPER_INFO_STAGE = "wallpaper-info-stage";
- @VisibleForTesting
- static final String WALLPAPER_BACKUP_DEVICE_INFO_STAGE = "wallpaper-backup-device-info-stage";
-
static final String EMPTY_SENTINEL = "empty";
static final String QUOTA_SENTINEL = "quota";
@@ -121,11 +110,6 @@ public class WallpaperBackupAgent extends BackupAgent {
static final String SYSTEM_GENERATION = "system_gen";
static final String LOCK_GENERATION = "lock_gen";
- /**
- * An approximate area threshold to compare device dimension similarity
- */
- static final int AREA_THRESHOLD = 50; // TODO: determine appropriate threshold
-
// If this file exists, it means we exceeded our quota last time
private File mQuotaFile;
private boolean mQuotaExceeded;
@@ -137,8 +121,6 @@ public class WallpaperBackupAgent extends BackupAgent {
private boolean mSystemHasLiveComponent;
private boolean mLockHasLiveComponent;
- private DisplayManager mDisplayManager;
-
@Override
public void onCreate() {
if (DEBUG) {
@@ -155,8 +137,6 @@ public class WallpaperBackupAgent extends BackupAgent {
mBackupManager = new BackupManager(getBaseContext());
mEventLogger = new WallpaperEventLogger(mBackupManager, /* wallpaperAgent */ this);
-
- mDisplayManager = getSystemService(DisplayManager.class);
}
@Override
@@ -195,7 +175,6 @@ public class WallpaperBackupAgent extends BackupAgent {
mSystemHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_SYSTEM) != null;
mLockHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_LOCK) != null;
- backupDeviceInfoFile(data);
backupWallpaperInfoFile(/* sysOrLockChanged= */ sysChanged || lockChanged, data);
backupSystemWallpaperFile(sharedPrefs, sysChanged, sysGeneration, data);
backupLockWallpaperFileIfItExists(sharedPrefs, lockChanged, lockGeneration, data);
@@ -212,50 +191,6 @@ public class WallpaperBackupAgent extends BackupAgent {
}
}
- /**
- * This method backs up the device dimension information. The device data will always get
- * overwritten when triggering a backup
- */
- private void backupDeviceInfoFile(FullBackupDataOutput data)
- throws IOException {
- final File deviceInfoStage = new File(getFilesDir(), WALLPAPER_BACKUP_DEVICE_INFO_STAGE);
-
- // save the dimensions of the device with xml formatting
- Point dimensions = getScreenDimensions();
- Point secondaryDimensions = getRealSize(getSmallerDisplay());
-
- deviceInfoStage.createNewFile();
- FileOutputStream fstream = new FileOutputStream(deviceInfoStage, false);
- TypedXmlSerializer out = Xml.resolveSerializer(fstream);
- out.startDocument(null, true);
- out.startTag(null, "dimensions");
-
- out.startTag(null, "width");
- out.text(String.valueOf(dimensions.x));
- out.endTag(null, "width");
-
- out.startTag(null, "height");
- out.text(String.valueOf(dimensions.y));
- out.endTag(null, "height");
-
- out.startTag(null, "secondarywidth");
- out.text(String.valueOf(secondaryDimensions != null ? secondaryDimensions.x : 0));
- out.endTag(null, "secondarywidth");
-
- out.startTag(null, "secondaryheight");
- out.text(String.valueOf(secondaryDimensions != null ? secondaryDimensions.y : 0));
- out.endTag(null, "secondaryheight");
-
- out.endTag(null, "dimensions");
- out.endDocument();
- fstream.flush();
- FileUtils.sync(fstream);
- fstream.close();
-
- if (DEBUG) Slog.v(TAG, "Storing device dimension data");
- backupFile(deviceInfoStage, data);
- }
-
private void backupWallpaperInfoFile(boolean sysOrLockChanged, FullBackupDataOutput data)
throws IOException {
final ParcelFileDescriptor wallpaperInfoFd = mWallpaperManager.getWallpaperInfoFile();
@@ -429,22 +364,9 @@ public class WallpaperBackupAgent extends BackupAgent {
final File infoStage = new File(filesDir, WALLPAPER_INFO_STAGE);
final File imageStage = new File(filesDir, SYSTEM_WALLPAPER_STAGE);
final File lockImageStage = new File(filesDir, LOCK_WALLPAPER_STAGE);
- final File deviceDimensionsStage = new File(filesDir, WALLPAPER_BACKUP_DEVICE_INFO_STAGE);
boolean lockImageStageExists = lockImageStage.exists();
try {
- // Parse the device dimensions of the source device and compare with target to
- // to identify whether we need to skip the remainder of the restore process
- Pair<Point, Point> sourceDeviceDimensions = parseDeviceDimensions(
- deviceDimensionsStage);
-
- Point targetDeviceDimensions = getScreenDimensions();
- if (sourceDeviceDimensions != null
- && isSourceDeviceSignificantlySmallerThanTarget(sourceDeviceDimensions.first,
- targetDeviceDimensions)) {
- Slog.d(TAG, "The source device is significantly smaller than target");
- }
-
// First parse the live component name so that we know for logging if we care about
// logging errors with the image restore.
ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
@@ -478,7 +400,6 @@ public class WallpaperBackupAgent extends BackupAgent {
infoStage.delete();
imageStage.delete();
lockImageStage.delete();
- deviceDimensionsStage.delete();
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit()
@@ -488,66 +409,6 @@ public class WallpaperBackupAgent extends BackupAgent {
}
}
- /**
- * This method parses the given file for the backed up device dimensions
- *
- * @param deviceDimensions the file which holds the device dimensions
- * @return the backed up device dimensions
- */
- private Pair<Point, Point> parseDeviceDimensions(File deviceDimensions) {
- int width = 0, height = 0, secondaryHeight = 0, secondaryWidth = 0;
- try {
- TypedXmlPullParser parser = Xml.resolvePullParser(
- new FileInputStream(deviceDimensions));
-
- while (parser.next() != XmlPullParser.END_TAG) {
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- continue;
- }
-
- String name = parser.getName();
-
- switch (name) {
- case "width":
- String widthText = readText(parser);
- width = Integer.valueOf(widthText);
- break;
-
- case "height":
- String textHeight = readText(parser);
- height = Integer.valueOf(textHeight);
- break;
-
- case "secondarywidth":
- String secondaryWidthText = readText(parser);
- secondaryWidth = Integer.valueOf(secondaryWidthText);
- break;
-
- case "secondaryheight":
- String secondaryHeightText = readText(parser);
- secondaryHeight = Integer.valueOf(secondaryHeightText);
- break;
- default:
- break;
- }
- }
- return new Pair<>(new Point(width, height), new Point(secondaryWidth, secondaryHeight));
-
- } catch (Exception e) {
- return null;
- }
- }
-
- private static String readText(TypedXmlPullParser parser)
- throws IOException, XmlPullParserException {
- String result = "";
- if (parser.next() == XmlPullParser.TEXT) {
- result = parser.getText();
- parser.nextTag();
- }
- return result;
- }
-
@VisibleForTesting
void updateWallpaperComponent(ComponentName wpService, int which)
throws IOException {
@@ -639,7 +500,6 @@ public class WallpaperBackupAgent extends BackupAgent {
mEventLogger.onLockImageWallpaperRestoreFailed(error);
}
}
-
private Rect parseCropHint(File wallpaperInfo, String sectionTag) {
Rect cropHint = new Rect();
try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
@@ -677,7 +537,7 @@ public class WallpaperBackupAgent extends BackupAgent {
if (type != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (!sectionTag.equals(tag)) continue;
- for (Pair<Integer, String> pair : List.of(
+ for (Pair<Integer, String> pair: List.of(
new Pair<>(WallpaperManager.PORTRAIT, "Portrait"),
new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"),
new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"),
@@ -831,94 +691,6 @@ public class WallpaperBackupAgent extends BackupAgent {
};
}
- /**
- * This method retrieves the dimensions of the largest display of the device
- *
- * @return a @{Point} object that contains the dimensions of the largest display on the device
- */
- private Point getScreenDimensions() {
- Point largetDimensions = null;
- int maxArea = 0;
-
- for (Display display : getInternalDisplays()) {
- Point displaySize = getRealSize(display);
-
- int width = displaySize.x;
- int height = displaySize.y;
- int area = width * height;
-
- if (area > maxArea) {
- maxArea = area;
- largetDimensions = displaySize;
- }
- }
-
- return largetDimensions;
- }
-
- private Point getRealSize(Display display) {
- DisplayInfo displayInfo = new DisplayInfo();
- display.getDisplayInfo(displayInfo);
- return new Point(displayInfo.logicalWidth, displayInfo.logicalHeight);
- }
-
- /**
- * This method returns the smaller display on a multi-display device
- *
- * @return Display that corresponds to the smaller display on a device or null if ther is only
- * one Display on a device
- */
- private Display getSmallerDisplay() {
- List<Display> internalDisplays = getInternalDisplays();
- Point largestDisplaySize = getScreenDimensions();
-
- // Find the first non-matching internal display
- for (Display display : internalDisplays) {
- Point displaySize = getRealSize(display);
- if (displaySize.x != largestDisplaySize.x || displaySize.y != largestDisplaySize.y) {
- return display;
- }
- }
-
- // If no smaller display found, return null, as there is only a single display
- return null;
- }
-
- /**
- * This method retrieves the collection of Display objects available in the device.
- * i.e. non-external displays are ignored
- *
- * @return list of displays corresponding to each display in the device
- */
- private List<Display> getInternalDisplays() {
- Display[] allDisplays = mDisplayManager.getDisplays(
- DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
-
- List<Display> internalDisplays = new ArrayList<>();
- for (Display display : allDisplays) {
- if (display.getType() == Display.TYPE_INTERNAL) {
- internalDisplays.add(display);
- }
- }
- return internalDisplays;
- }
-
- /**
- * This method compares the source and target dimensions, and returns true if there is a
- * significant difference in area between them and the source dimensions are smaller than the
- * target dimensions.
- *
- * @param sourceDimensions is the dimensions of the source device
- * @param targetDimensions is the dimensions of the target device
- */
- @VisibleForTesting
- boolean isSourceDeviceSignificantlySmallerThanTarget(Point sourceDimensions,
- Point targetDimensions) {
- int rawAreaDelta = (targetDimensions.x * targetDimensions.y)
- - (sourceDimensions.x * sourceDimensions.y);
- return rawAreaDelta > AREA_THRESHOLD;
- }
-
@VisibleForTesting
boolean isDeviceInRestore() {
try {
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 79e7bf04dadf..3ecdf3f101a5 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -59,7 +59,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
@@ -677,7 +676,7 @@ public class WallpaperBackupAgentTest {
mWallpaperBackupAgent.onRestoreFinished();
- for (String wallpaper : List.of(WALLPAPER_IMG_LOCK, WALLPAPER_IMG_SYSTEM)) {
+ for (String wallpaper: List.of(WALLPAPER_IMG_LOCK, WALLPAPER_IMG_SYSTEM)) {
DataTypeResult result = getLoggingResult(wallpaper,
mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
assertThat(result).isNotNull();
@@ -841,26 +840,6 @@ public class WallpaperBackupAgentTest {
testParseCropHints(testMap);
}
- @Test
- public void test_sourceDimensionsAreLargerThanTarget() {
- // source device is larger than target, expecting to get false
- Point sourceDimensions = new Point(2208, 1840);
- Point targetDimensions = new Point(1080, 2092);
- boolean isSourceSmaller = mWallpaperBackupAgent
- .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
- assertThat(isSourceSmaller).isEqualTo(false);
- }
-
- @Test
- public void test_sourceDimensionsMuchSmallerThanTarget() {
- // source device is smaller than target, expecting to get true
- Point sourceDimensions = new Point(1080, 2092);
- Point targetDimensions = new Point(2208, 1840);
- boolean isSourceSmaller = mWallpaperBackupAgent
- .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
- assertThat(isSourceSmaller).isEqualTo(true);
- }
-
private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception {
assumeTrue(multiCrop());
mockRestoredStaticWallpaperFile(testMap);
@@ -955,7 +934,7 @@ public class WallpaperBackupAgentTest {
TypedXmlSerializer out = Xml.resolveSerializer(fstream);
out.startDocument(null, true);
out.startTag(null, "wp");
- for (Map.Entry<Integer, Rect> entry : crops.entrySet()) {
+ for (Map.Entry<Integer, Rect> entry: crops.entrySet()) {
String orientation = switch (entry.getKey()) {
case WallpaperManager.PORTRAIT -> "Portrait";
case WallpaperManager.LANDSCAPE -> "Landscape";
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index e535f0a05a02..178102e2876d 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -86,6 +86,21 @@ java_library {
jarjar_rules: ":ravenwood-services-jarjar-rules",
}
+// Separated out from ravenwood-junit-impl since it needs to compile
+// against `module_current`
+java_library {
+ name: "ravenwood-junit-impl-flag",
+ srcs: [
+ "junit-flag-src/**/*.java",
+ ],
+ sdk_version: "module_current",
+ libs: [
+ "junit",
+ "flag-junit",
+ ],
+ visibility: ["//visibility:public"],
+}
+
// Carefully compiles against only test_current to support tests that
// want to verify they're unbundled. The "impl" library above is what
// ships inside the Ravenwood environment to actually drive any API
@@ -95,10 +110,12 @@ java_library {
srcs: [
"junit-src/**/*.java",
"junit-stub-src/**/*.java",
+ "junit-flag-src/**/*.java",
],
sdk_version: "test_current",
libs: [
"junit",
+ "flag-junit",
],
visibility: ["//visibility:public"],
}
diff --git a/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java b/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
new file mode 100644
index 000000000000..9d6277473298
--- /dev/null
+++ b/ravenwood/junit-flag-src/android/platform/test/flag/junit/RavenwoodFlagsValueProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.test.flag.junit;
+
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.IFlagsValueProvider;
+
+/**
+ * Offer to create {@link CheckFlagsRule} instances that are useful on the Ravenwood deviceless
+ * testing environment.
+ *
+ * At the moment, default flag values are not available on Ravenwood, so the only options offered
+ * here are "all-on" and "all-off" options. Tests that want to exercise specific flag states should
+ * use {@link android.platform.test.flag.junit.SetFlagsRule}.
+ */
+public class RavenwoodFlagsValueProvider {
+ /**
+ * Create a {@link CheckFlagsRule} instance where flags are in an "all-on" state.
+ */
+ public static CheckFlagsRule createAllOnCheckFlagsRule() {
+ return new CheckFlagsRule(new IFlagsValueProvider() {
+ @Override
+ public boolean getBoolean(String flag) {
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Create a {@link CheckFlagsRule} instance where flags are in an "all-off" state.
+ */
+ public static CheckFlagsRule createAllOffCheckFlagsRule() {
+ return new CheckFlagsRule(new IFlagsValueProvider() {
+ @Override
+ public boolean getBoolean(String flag) {
+ return false;
+ }
+ });
+ }
+}
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 9179a621d0df..7c0cee812996 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -112,6 +112,24 @@ public class MyCodeTest {
This naturally composes together well with any `RavenwoodRule` that your test may need.
+While `SetFlagsRule` is generally a best-practice (as it can explicitly confirm behaviors for both "on" and "off" states), you may need to write tests that use `CheckFlagsRule` (such as when writing CTS). Ravenwood currently supports `CheckFlagsRule` by offering "all-on" and "all-off" behaviors:
+
+```
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+@RunWith(AndroidJUnit4.class)
+public class MyCodeTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
+```
+
+Ravenwood currently doesn't have knowledge of the "default" value of any flags, so using `createAllOnCheckFlagsRule()` is recommended to verify the widest possible set of behaviors. The example code above falls back to using default values from `DeviceFlagsValueProvider` when not running on Ravenwood.
+
## Strategies for migration/bivalent tests
Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can be dual-compiled to run on both a real Android device and under a Ravenwood environment.
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 19dd7b7ea2f6..d4f04b5ad760 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3713,7 +3713,7 @@ public class AudioService extends IAudioService.Stub
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume: postSetAvrcpAbsoluteVolumeIndex index="
+ Log.d(TAG, "adjustStreamVolume: postSetAvrcpAbsoluteVolumeIndex index="
+ newIndex + "stream=" + streamType);
}
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
@@ -3727,7 +3727,7 @@ public class AudioService extends IAudioService.Stub
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ Log.d(TAG, "adjustStreamVolume postSetLeAudioVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
mDeviceBroker.postSetLeAudioVolumeIndex(newIndex,
@@ -3740,7 +3740,7 @@ public class AudioService extends IAudioService.Stub
// the one expected by the hearing aid
if (streamType == getBluetoothContextualVolumeStream()) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
+ Log.d(TAG, "adjustStreamVolume postSetHearingAidVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
@@ -4722,7 +4722,7 @@ public class AudioService extends IAudioService.Stub
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
- Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ Log.d(TAG, "setStreamVolume postSetLeAudioVolumeIndex index="
+ index + " stream=" + streamType);
}
mDeviceBroker.postSetLeAudioVolumeIndex(index, mStreamStates[streamType].getMaxIndex(),
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 68b4e3fb51ba..6b8586a2e483 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -119,7 +119,7 @@ public class FaceService extends SystemService {
* Receives the incoming binder calls from FaceManager.
*/
@VisibleForTesting final class FaceServiceWrapper extends IFaceService.Stub {
- @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index fb4976d3cef2..b2a738fd352b 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -463,12 +463,6 @@ public class AutomaticBrightnessController {
boolean userChangedAutoBrightnessAdjustment, int displayPolicy,
boolean shouldResetShortTermModel) {
mState = state;
- // While dozing, the application processor may be suspended which will prevent us from
- // receiving new information from the light sensor. On some devices, we may be able to
- // switch to a wake-up light sensor instead but for now we will simply disable the sensor
- // and hold onto the last computed screen auto brightness. We save the dozing flag for
- // debugging purposes.
- boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
boolean changed = setBrightnessConfiguration(configuration, shouldResetShortTermModel);
changed |= setDisplayPolicy(displayPolicy);
if (userChangedAutoBrightnessAdjustment) {
@@ -482,10 +476,10 @@ public class AutomaticBrightnessController {
}
final boolean userInitiatedChange =
userChangedBrightness || userChangedAutoBrightnessAdjustment;
- if (userInitiatedChange && enable && !dozing) {
+ if (userInitiatedChange && enable) {
prepareBrightnessAdjustmentSample();
}
- changed |= setLightSensorEnabled(enable && !dozing);
+ changed |= setLightSensorEnabled(enable);
if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) {
// Maximum brightness has changed, so recalculate display brightness.
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 76f303596bdb..2010aca72494 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1485,8 +1485,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
// Use default brightness when dozing unless overridden.
- if ((Float.isNaN(brightnessState))
- && Display.isDozeState(state)) {
+ if (Float.isNaN(brightnessState) && mPowerRequest.policy == POLICY_DOZE) {
rawBrightnessState = mScreenBrightnessDozeConfig;
brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 8eaecef6e562..d1ca49b8bf79 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -15,6 +15,8 @@
*/
package com.android.server.display.brightness.strategy;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
@@ -102,8 +104,7 @@ public class AutomaticBrightnessStrategy {
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
final boolean autoBrightnessEnabledInDoze =
- allowAutoBrightnessWhileDozingConfig
- && Display.isDozeState(targetDisplayState);
+ allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE;
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
&& (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index f1698dd0f025..8826e3d2d345 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.ResultReceiver;
import android.util.EventLog;
import android.util.Slog;
+import android.view.MotionEvent;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
@@ -75,7 +76,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ public void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
@@ -88,7 +89,8 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) {
if (DEBUG_IME_VISIBILITY) {
- EventLog.writeEvent(IMF_SHOW_IME, statsToken.getTag(),
+ EventLog.writeEvent(IMF_SHOW_IME,
+ statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE,
Objects.toString(mService.mCurFocusedWindow),
InputMethodDebug.softInputDisplayReasonToString(reason),
InputMethodDebug.softInputModeToString(
@@ -102,7 +104,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public void performHideIme(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
+ public void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
if (curMethod != null) {
@@ -118,7 +120,8 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
if (curMethod.hideSoftInput(hideInputToken, statsToken, 0, resultReceiver)) {
if (DEBUG_IME_VISIBILITY) {
- EventLog.writeEvent(IMF_HIDE_IME, statsToken.getTag(),
+ EventLog.writeEvent(IMF_HIDE_IME,
+ statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE,
Objects.toString(mService.mCurFocusedWindow),
InputMethodDebug.softInputDisplayReasonToString(reason),
InputMethodDebug.softInputModeToString(
@@ -132,14 +135,16 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state) {
- applyImeVisibility(windowToken, statsToken, state, -1 /* ignore reason */);
+ applyImeVisibility(windowToken, statsToken, state,
+ SoftInputShowHideReason.NOT_SET /* ignore reason */);
}
@GuardedBy("ImfLock.class")
void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state, int reason) {
+ @ImeVisibilityStateComputer.VisibilityState int state,
+ @SoftInputShowHideReason int reason) {
switch (state) {
case STATE_SHOW_IME:
ImeTracker.forLogging().onProgress(statsToken,
@@ -164,18 +169,20 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
}
break;
case STATE_HIDE_IME_EXPLICIT:
- mService.hideCurrentInputLocked(windowToken, statsToken, 0, null, reason);
+ mService.hideCurrentInputLocked(windowToken, statsToken,
+ 0 /* flags */, null /* resultReceiver */, reason);
break;
case STATE_HIDE_IME_NOT_ALWAYS:
mService.hideCurrentInputLocked(windowToken, statsToken,
- InputMethodManager.HIDE_NOT_ALWAYS, null, reason);
+ InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason);
break;
case STATE_SHOW_IME_IMPLICIT:
mService.showCurrentInputLocked(windowToken, statsToken,
- InputMethodManager.SHOW_IMPLICIT, null, reason);
+ InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN,
+ null /* resultReceiver */, reason);
break;
case STATE_SHOW_IME_SNAPSHOT:
- showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked(), null);
+ showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked());
break;
case STATE_REMOVE_IME_SNAPSHOT:
removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
@@ -187,11 +194,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
- public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
- @Nullable ImeTracker.Token statsToken) {
+ public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
- SHOW_IME_SCREENSHOT_FROM_IMMS, statsToken);
+ SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */);
return true;
}
return false;
@@ -202,7 +208,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
public boolean removeImeScreenshot(int displayId) {
if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow,
- REMOVE_IME_SCREENSHOT_FROM_IMMS, null);
+ REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */);
return true;
}
return false;
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 776184fb098c..a380bc1ca171 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -201,7 +201,7 @@ final class IInputMethodInvoker {
// TODO(b/192412909): Convert this back to void method
@AnyThread
- boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ boolean showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
try {
mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver);
@@ -214,8 +214,8 @@ final class IInputMethodInvoker {
// TODO(b/192412909): Convert this back to void method
@AnyThread
- boolean hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, int flags,
- ResultReceiver resultReceiver) {
+ boolean hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
+ int flags, ResultReceiver resultReceiver) {
try {
mTarget.hideSoftInput(hideInputToken, statsToken, flags, resultReceiver);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index d06c31cf36f0..85ab77355c9a 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -75,34 +75,12 @@ public final class ImeTrackerService extends IImeTracker.Stub {
@NonNull
@Override
- public ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
+ public ImeTracker.Token onStart(@NonNull String tag, int uid, @ImeTracker.Type int type,
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
final var binder = new Binder();
final var token = new ImeTracker.Token(binder, tag);
- final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN,
- origin, reason, fromUser);
- synchronized (mLock) {
- mHistory.addEntry(binder, entry);
-
- // Register a delayed task to handle the case where the new entry times out.
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT,
- ImeTracker.PHASE_NOT_SET);
- }
- }, TIMEOUT_MS);
- }
- return token;
- }
-
- @NonNull
- @Override
- public ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
- final var binder = new Binder();
- final var token = new ImeTracker.Token(binder, tag);
- final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN,
- origin, reason, fromUser);
+ final var entry = new History.Entry(tag, uid, type, ImeTracker.STATUS_RUN, origin, reason,
+ fromUser);
synchronized (mLock) {
mHistory.addEntry(binder, entry);
@@ -158,7 +136,7 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/**
* Updates the IME request tracking token with new information available in IMMS.
*
- * @param statsToken the token corresponding to the current IME request.
+ * @param statsToken the token tracking the current IME request.
* @param requestWindowName the name of the window that created the IME request.
*/
public void onImmsUpdate(@NonNull ImeTracker.Token statsToken,
@@ -223,7 +201,7 @@ public final class ImeTrackerService extends IImeTracker.Stub {
* Sets the live entry corresponding to the tracking token, if it exists, as finished,
* and uploads the data for metrics.
*
- * @param statsToken the token corresponding to the current IME request.
+ * @param statsToken the token tracking the current IME request.
* @param status the finish status of the IME request.
* @param phase the phase the IME request finished at, if it exists
* (or {@link ImeTracker#PHASE_NOT_SET} otherwise).
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index 29fa36982351..9f2b84d9bfa5 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -17,7 +17,6 @@
package com.android.server.inputmethod;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.view.inputmethod.ImeTracker;
@@ -34,12 +33,12 @@ interface ImeVisibilityApplier {
* Performs showing IME on top of the given window.
*
* @param showInputToken A token that represents the requester to show IME.
- * @param statsToken A token that tracks the progress of an IME request.
+ * @param statsToken The token tracking the current IME request.
* @param resultReceiver If non-null, this will be called back to the caller when
* it has processed request to tell what it has done.
* @param reason The reason for requesting to show IME.
*/
- default void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ default void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {}
@@ -47,12 +46,12 @@ interface ImeVisibilityApplier {
* Performs hiding IME to the given window
*
* @param hideInputToken A token that represents the requester to hide IME.
- * @param statsToken A token that tracks the progress of an IME request.
+ * @param statsToken The token tracking the current IME request.
* @param resultReceiver If non-null, this will be called back to the caller when
* it has processed request to tell what it has done.
* @param reason The reason for requesting to hide IME.
*/
- default void performHideIme(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
+ default void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {}
/**
@@ -60,10 +59,10 @@ interface ImeVisibilityApplier {
* according to the given visibility state.
*
* @param windowToken The token of a window for applying the IME visibility
- * @param statsToken A token that tracks the progress of an IME request.
+ * @param statsToken The token tracking the current IME request.
* @param state The new IME visibility state for the applier to handle
*/
- default void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state) {}
/**
@@ -84,11 +83,9 @@ interface ImeVisibilityApplier {
*
* @param windowToken The token of a window to show the IME screenshot.
* @param displayId The unique id to identify the display
- * @param statsToken A token that tracks the progress of an IME request.
* @return {@code true} if success, {@code false} otherwise.
*/
- default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId,
- @Nullable ImeTracker.Token statsToken) {
+ default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId) {
return false;
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 0dd48ae6c9e1..743b8e382347 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -212,9 +212,11 @@ public final class ImeVisibilityStateComputer {
boolean visibleRequested, boolean removed) {
if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed)
&& mCurVisibleImeLayeringOverlay != null) {
- mService.onApplyImeVisibilityFromComputer(imeInputTarget,
- new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
- SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE));
+ final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */);
+ mService.onApplyImeVisibilityFromComputer(imeInputTarget, statsToken,
+ new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason));
}
mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null;
}
@@ -224,7 +226,7 @@ public final class ImeVisibilityStateComputer {
/**
* Called when {@link InputMethodManagerService} is processing the show IME request.
*
- * @param statsToken The token for tracking this show request.
+ * @param statsToken The token tracking the current IME request.
* @return {@code true} when the show request can proceed.
*/
boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken,
@@ -250,7 +252,7 @@ public final class ImeVisibilityStateComputer {
/**
* Called when {@link InputMethodManagerService} is processing the hide IME request.
*
- * @param statsToken The token for tracking this hide request.
+ * @param statsToken The token tracking the current IME request.
* @return {@code true} when the hide request can proceed.
*/
boolean canHideIme(@NonNull ImeTracker.Token statsToken,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3dedca9c42ba..1564b2f86218 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -578,7 +578,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return mBindingController.hasMainConnection();
}
- /** The token tracking the current IME request or {@code null} otherwise. */
+ /**
+ * The token tracking the current IME show request that is waiting for a connection to an IME,
+ * otherwise {@code null}.
+ */
@Nullable
private ImeTracker.Token mCurStatsToken;
@@ -1128,11 +1131,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(
accessibilitySoftKeyboardSetting);
if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
- 0 /* flags */, null /* resultReceiver */,
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
} else if (isShowRequestedForCurrentWindow()) {
- showCurrentInputImplicitLocked(mCurFocusedWindow,
+ showCurrentInputLocked(mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT,
SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE);
}
} else if (stylusHandwritingEnabledUri.equals(uri)) {
@@ -1627,8 +1629,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
// Hide soft input before user switch task since switch task may block main handler a while
// and delayed the hideCurrentInputLocked().
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */, SoftInputShowHideReason.HIDE_SWITCH_USER);
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_SWITCH_USER);
final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
clientToBeReset);
mUserSwitchHandlerTask = task;
@@ -2216,8 +2218,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
clearClientSessionLocked(client);
clearClientSessionForAccessibilityLocked(client);
if (mCurClient == client) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
if (mBoundToMethod) {
mBoundToMethod = false;
IInputMethodInvoker curMethod = getCurMethodLocked();
@@ -2291,8 +2293,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// service, that wouldn't make the attached IME token validity check in time)
// As a result, we have to notify WM to apply IME visibility before clearing the
// binding states in the first place.
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, mCurStatsToken,
- STATE_HIDE_IME);
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+ SoftInputShowHideReason.UNBIND_CURRENT_METHOD);
+ mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken, STATE_HIDE_IME);
}
}
@@ -2369,10 +2372,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (isShowRequestedForCurrentWindow()) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
// Re-use current statsToken, if it exists.
- final ImeTracker.Token statsToken = mCurStatsToken;
+ final var statsToken = mCurStatsToken != null ? mCurStatsToken
+ : createStatsTokenForFocusedClient(true /* show */,
+ SoftInputShowHideReason.ATTACH_NEW_INPUT);
mCurStatsToken = null;
showCurrentInputLocked(mCurFocusedWindow, statsToken,
- mVisibilityStateComputer.getShowFlags(),
+ mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN,
null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
@@ -2464,8 +2469,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */,
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE);
return InputBindResult.NO_IME;
}
@@ -3385,8 +3389,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickTooType, ResultReceiver resultReceiver,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickToolType, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
@@ -3402,7 +3406,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
- return showCurrentInputLocked(windowToken, statsToken, flags, lastClickTooType,
+ return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType,
resultReceiver, reason);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -3644,24 +3648,18 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- @InputMethodManager.ShowFlags int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ private boolean showCurrentInputLocked(IBinder windowToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) {
+ final var statsToken = createStatsTokenForFocusedClient(true /* show */, reason);
return showCurrentInputLocked(windowToken, statsToken, flags,
- MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
+ MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason);
}
@GuardedBy("ImfLock.class")
- private boolean showCurrentInputLocked(IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
- int lastClickToolType, ResultReceiver resultReceiver,
+ boolean showCurrentInputLocked(IBinder windowToken,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
- // Create statsToken is none exists.
- if (statsToken == null) {
- statsToken = createStatsTokenForFocusedClient(true /* show */,
- ImeTracker.ORIGIN_SERVER_START_INPUT, reason, false /* fromUser */);
- }
-
if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) {
return false;
}
@@ -3699,7 +3697,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3728,17 +3726,29 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- @GuardedBy("ImfLock.class")
- boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- // Create statsToken is none exists.
- if (statsToken == null) {
- final boolean fromUser = reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY;
- statsToken = createStatsTokenForFocusedClient(false /* show */,
- ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason, fromUser);
+ @Override
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() {
+ super.hideSoftInputFromServerForTest_enforcePermission();
+
+ synchronized (ImfLock.class) {
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT);
}
+ }
+
+ @GuardedBy("ImfLock.class")
+ private boolean hideCurrentInputLocked(IBinder windowToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) {
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */, reason);
+ return hideCurrentInputLocked(windowToken, statsToken, flags, null /* resultReceiver */,
+ reason);
+ }
+ @GuardedBy("ImfLock.class")
+ boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) {
return false;
}
@@ -3915,9 +3925,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
+ " a background user, use EditorInfo.targetInputMethodUser with"
+ " INTERACT_ACROSS_USERS_FULL permission.");
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
- 0 /* flags */,
- null /* resultReceiver */,
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_INVALID_USER);
return InputBindResult.INVALID_USER;
}
@@ -4025,11 +4033,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.computeState(windowState,
isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags));
if (imeVisRes != null) {
+ boolean isShow = false;
switch (imeVisRes.getReason()) {
case SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY:
case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV:
case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE:
+ isShow = true;
+
if (editorInfo != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, editorInfo, startInputFlags,
@@ -4039,8 +4050,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
break;
}
-
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null /* statsToken */,
+ final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason());
+ mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken,
imeVisRes.getState(), imeVisRes.getReason());
if (imeVisRes.getReason() == SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW) {
@@ -4068,13 +4079,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- private void showCurrentInputImplicitLocked(@NonNull IBinder windowToken,
- @SoftInputShowHideReason int reason) {
- showCurrentInputLocked(windowToken, null /* statsToken */, InputMethodManager.SHOW_IMPLICIT,
- null /* resultReceiver */, reason);
- }
-
- @GuardedBy("ImfLock.class")
private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName,
@Nullable ImeTracker.Token statsToken) {
if (mCurClient == null || client == null
@@ -4776,15 +4780,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
ImeTracker.forLogging().onFailed(statsToken,
- ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(
windowToken);
mVisibilityApplier.applyImeVisibility(requestToken, statsToken,
@@ -4862,17 +4868,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- private void hideMySoftInput(@NonNull IBinder token, @InputMethodManager.HideFlags int flags,
- @SoftInputShowHideReason int reason) {
+ private void hideMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
final long ident = Binder.clearCallingIdentity();
try {
- hideCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags,
+ hideCurrentInputLocked(mLastImeTargetWindow, statsToken, flags,
null /* resultReceiver */, reason);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4884,18 +4894,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- private void showMySoftInput(@NonNull IBinder token, @InputMethodManager.ShowFlags int flags) {
+ private void showMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME);
final long ident = Binder.clearCallingIdentity();
try {
- showCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags,
- null /* resultReceiver */,
- SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME);
+ showCurrentInputLocked(mLastImeTargetWindow, statsToken, flags,
+ MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4912,10 +4926,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- void onApplyImeVisibilityFromComputer(IBinder windowToken,
+ void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
@NonNull ImeVisibilityResult result) {
synchronized (ImfLock.class) {
- mVisibilityApplier.applyImeVisibility(windowToken, null, result.getState(),
+ mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(),
result.getReason());
}
}
@@ -5016,9 +5030,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case MSG_HIDE_ALL_INPUT_METHODS:
synchronized (ImfLock.class) {
- final @SoftInputShowHideReason int reason = (int) msg.obj;
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
- null /* resultReceiver */, reason);
+ @SoftInputShowHideReason final int reason = (int) msg.obj;
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, reason);
}
return true;
@@ -5176,7 +5189,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
mCurFocusedWindow, interactive);
if (imeVisRes != null) {
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null,
+ // Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker.
+ mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null /* statsToken */,
imeVisRes.getState(), imeVisRes.getReason());
}
// Eligible IME processes use new "setInteractive" protocol.
@@ -6684,8 +6698,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getUserId()) {
- hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
- 0 /* flags */, null /* resultReceiver */,
+ hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
mBindingController.unbindCurrentMethod();
@@ -6814,13 +6827,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
* Creates an IME request tracking token for the current focused client.
*
* @param show whether this is a show or a hide request.
- * @param origin the origin of the IME request.
* @param reason the reason why the IME request was created.
- * @param fromUser whether this request was created directly from user interaction.
*/
@NonNull
private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
- @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) {
+ @SoftInputShowHideReason int reason) {
final int uid = mCurFocusedWindowClient != null
? mCurFocusedWindowClient.mUid
: -1;
@@ -6828,13 +6839,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
? mCurFocusedWindowEditorInfo.packageName
: "uid(" + uid + ")";
- if (show) {
- return ImeTracker.forLogging()
- .onRequestShow(packageName, uid, origin, reason, fromUser);
- } else {
- return ImeTracker.forLogging()
- .onRequestHide(packageName, uid, origin, reason, fromUser);
- }
+ return ImeTracker.forLogging().onStart(packageName, uid,
+ show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_SERVER,
+ reason, false /* fromUser */);
}
private static final class InputMethodPrivilegedOperationsImpl
@@ -6909,12 +6916,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
- @SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) {
+ public void hideMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason,
+ AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {
- mImms.hideMySoftInput(mToken, flags, reason);
+ mImms.hideMySoftInput(mToken, statsToken, flags, reason);
typedFuture.complete(null);
} catch (Throwable e) {
typedFuture.completeExceptionally(e);
@@ -6923,12 +6931,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void showMySoftInput(@InputMethodManager.ShowFlags int flags,
+ public void showMySoftInput(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason,
AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {
- mImms.showMySoftInput(mToken, flags);
+ mImms.showMySoftInput(mToken, statsToken, flags, reason);
typedFuture.complete(null);
} catch (Throwable e) {
typedFuture.completeExceptionally(e);
@@ -6987,7 +6996,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
mImms.applyImeVisibility(mToken, windowToken, setVisible, statsToken);
}
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index db6a9af0fe62..9caf5cfe67f3 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -184,6 +184,13 @@ public class ZeroJankProxy extends IInputMethodManager.Stub {
return true;
}
+ @Override
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ public void hideSoftInputFromServerForTest() throws RemoteException {
+ super.hideSoftInputFromServerForTest_enforcePermission();
+ mInner.hideSoftInputFromServerForTest();
+ }
+
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@Override
public void startInputOrWindowGainedFocusAsync(
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 1f7d5490dd6d..2a487856bfb8 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -851,7 +851,6 @@ class MediaRouter2ServiceImpl {
}
}
- @RequiresPermission(value = Manifest.permission.MEDIA_ROUTING_CONTROL, conditional = true)
private boolean checkMediaRoutingControlPermission(
int callerUid, int callerPid, @Nullable String callerPackageName) {
return PermissionChecker.checkPermissionForDataDelivery(
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 7af5c08f5ffa..1dc86f2b7f1e 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -56,7 +56,6 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
private boolean mIsClosed;
private final int mPid;
- private final ForegroundServiceDelegationOptions mForegroundServiceDelegationOptions;
public MediaSession2Record(
Session2Token sessionToken,
@@ -76,25 +75,6 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
.build();
mPid = pid;
mPolicies = policies;
- mForegroundServiceDelegationOptions =
- new ForegroundServiceDelegationOptions.Builder()
- .setClientPid(mPid)
- .setClientUid(getUid())
- .setClientPackageName(getPackageName())
- .setClientAppThread(null)
- .setSticky(false)
- .setClientInstanceName(
- "MediaSessionFgsDelegate_"
- + getUid()
- + "_"
- + mPid
- + "_"
- + getPackageName())
- .setForegroundServiceTypes(0)
- .setDelegationService(
- ForegroundServiceDelegationOptions
- .DELEGATION_SERVICE_MEDIA_PLAYBACK)
- .build();
}
}
@@ -119,7 +99,10 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
@Override
public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
- return mForegroundServiceDelegationOptions;
+ // For an app to be eligible for FGS delegation, it needs a media session liked to a media
+ // notification. Currently, notifications cannot be linked to MediaSession2 so it is not
+ // supported.
+ return null;
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 42851747c5cb..e2163c54f4e2 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -169,7 +169,8 @@ public class MediaSessionService extends SystemService implements Monitor {
private UsageStatsManagerInternal mUsageStatsManagerInternal;
/* Maps uid with all user engaging session tokens associated to it */
- private final SparseArray<Set<MediaSession.Token>> mUserEngagingSessions = new SparseArray<>();
+ private final SparseArray<Set<MediaSessionRecordImpl>> mUserEngagingSessions =
+ new SparseArray<>();
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -625,9 +626,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
record.getForegroundServiceDelegationOptions();
- if (foregroundServiceDelegationOptions == null
- || foregroundServiceDelegationOptions.mClientPid == Process.INVALID_PID) {
- // This record doesn't support FGS delegation. In practice, this is MediaSession2.
+ if (foregroundServiceDelegationOptions == null) {
return;
}
if (allowRunningInForeground) {
@@ -640,23 +639,21 @@ public class MediaSessionService extends SystemService implements Monitor {
}
private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) {
- if (!android.app.usage.Flags.userInteractionTypeApi()
- || !(record instanceof MediaSessionRecord)) {
+ if (!android.app.usage.Flags.userInteractionTypeApi()) {
return;
}
String packageName = record.getPackageName();
int sessionUid = record.getUid();
- MediaSession.Token token = ((MediaSessionRecord) record).getSessionToken();
if (userEngaged) {
if (!mUserEngagingSessions.contains(sessionUid)) {
mUserEngagingSessions.put(sessionUid, new HashSet<>());
reportUserInteractionEvent(
USAGE_STATS_ACTION_START, record.getUserId(), packageName);
}
- mUserEngagingSessions.get(sessionUid).add(token);
+ mUserEngagingSessions.get(sessionUid).add(record);
} else if (mUserEngagingSessions.contains(sessionUid)) {
- mUserEngagingSessions.get(sessionUid).remove(token);
+ mUserEngagingSessions.get(sessionUid).remove(record);
if (mUserEngagingSessions.get(sessionUid).isEmpty()) {
reportUserInteractionEvent(
USAGE_STATS_ACTION_STOP, record.getUserId(), packageName);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e9a7fe1371ac..ec4b38b10af2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3504,7 +3504,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
- statusbar.moveFocusedTaskToFullscreen(event.getDisplayId());
+ statusbar.moveFocusedTaskToFullscreen(getTargetDisplayIdForKeyEvent(event));
logKeyboardSystemsEvent(event, KeyboardLogEvent.MULTI_WINDOW_NAVIGATION);
return true;
}
@@ -3514,7 +3514,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
- statusbar.enterDesktop(event.getDisplayId());
+ statusbar.enterDesktop(getTargetDisplayIdForKeyEvent(event));
logKeyboardSystemsEvent(event, KeyboardLogEvent.DESKTOP_MODE);
return true;
}
@@ -6951,4 +6951,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
== PERMISSION_GRANTED;
}
}
+
+ private int getTargetDisplayIdForKeyEvent(KeyEvent event) {
+ int displayId = event.getDisplayId();
+
+ if (displayId == INVALID_DISPLAY) {
+ displayId = mTopFocusedDisplayId;
+ }
+
+ if (displayId == INVALID_DISPLAY) {
+ return DEFAULT_DISPLAY;
+ } else {
+ return displayId;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 860fe15f648a..7d5aa96db0b8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2894,6 +2894,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Makes starting window always fill the associated task. */
private void attachStartingSurfaceToAssociatedTask() {
+ if (mSyncState == SYNC_STATE_NONE && isEmbedded()) {
+ // Collect this activity since it's starting window will reparent to task. To ensure
+ // any starting window's transaction will occur in order.
+ mTransitionController.collect(this);
+ }
// Associate the configuration of starting window with the task.
overrideConfigurationPropagation(mStartingWindow, mStartingData.mAssociatedTask);
getSyncTransaction().reparent(mStartingWindow.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 218b7512b861..e283f3e8ef0e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4163,19 +4163,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
enforceTaskPermission("onPictureInPictureUiStateChanged");
- // The PictureInPictureUiState is sent to current pip task if there is any
- // -or- the top standard task (state like entering PiP does not require a pinned task).
- final Task task;
- if (mRootWindowContainer.getDefaultTaskDisplayArea().hasPinnedTask()) {
- task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootPinnedTask();
- } else {
- task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootTask(
- t -> t.isActivityTypeStandard());
- }
- if (task != null && task.getTopMostActivity() != null
- && !task.getTopMostActivity().isState(FINISHING, DESTROYING, DESTROYED)) {
- mWindowManager.mAtmService.mActivityClientController.onPictureInPictureUiStateChanged(
- task.getTopMostActivity(), pipState);
+ synchronized (mGlobalLock) {
+ // The PictureInPictureUiState is sent to current pip task if there is any
+ // -or- the top standard task (state like entering PiP does not require a pinned task).
+ final Task task;
+ if (mRootWindowContainer.getDefaultTaskDisplayArea().hasPinnedTask()) {
+ task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootPinnedTask();
+ } else {
+ task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootTask(
+ t -> t.isActivityTypeStandard());
+ }
+ if (task != null && task.getTopMostActivity() != null
+ && !task.getTopMostActivity().isState(FINISHING, DESTROYING, DESTROYED)) {
+ mWindowManager.mAtmService.mActivityClientController
+ .onPictureInPictureUiStateChanged(task.getTopMostActivity(), pipState);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index d1d498dbac46..2cda1f55b038 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -74,6 +74,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_TASK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.ClientLifecycleManager.shouldDispatchCompatClientTransactionIndependently;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -948,6 +949,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
// Schedule transaction.
+ if (shouldDispatchCompatClientTransactionIndependently(r.mTargetSdk)) {
+ // LaunchActivityItem has @UnsupportedAppUsage usages.
+ // Guard the bundleClientTransactionFlag feature with targetSDK on Android 15+.
+ // To not bundle the transaction, dispatch the pending before schedule new
+ // transaction.
+ mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread());
+ }
mService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
proc.getThread(), launchActivityItem, lifecycleItem,
// Immediately dispatch the transaction, so that if it fails, the server can
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index e48e4e84d60d..816fe1dbae94 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -22,6 +22,7 @@ import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -179,6 +180,22 @@ class ClientLifecycleManager {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
+ /** Executes the pending transaction for the given client process. */
+ void dispatchPendingTransaction(@NonNull IApplicationThread client) {
+ if (!Flags.bundleClientTransactionFlag()) {
+ return;
+ }
+ final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
+ if (pendingTransaction != null) {
+ try {
+ scheduleTransaction(pendingTransaction);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to deliver pending transaction", e);
+ // TODO(b/323801078): apply cleanup for individual transaction item if needed.
+ }
+ }
+ }
+
/**
* Called to when {@link WindowSurfacePlacer#continueLayout}.
* Dispatches all pending transactions unless there is an ongoing/scheduled layout, in which
@@ -233,4 +250,17 @@ class ClientLifecycleManager {
&& !mWms.mWindowPlacerLocked.isTraversalScheduled()
&& !mWms.mWindowPlacerLocked.isInLayout();
}
+
+ /**
+ * Guards the bundleClientTransactionFlag feature with targetSDK on Android 15+.
+ *
+ * Suppressing because it can't guard with @EnabledSince on VANILLA_ICE_CREAM yet since the
+ * version is not published.
+ *
+ * TODO(b/324203798): update in V
+ */
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ static boolean shouldDispatchCompatClientTransactionIndependently(int appTargetSdk) {
+ return appTargetSdk <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index ea31e632cfb8..9fee3433f6be 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -215,11 +215,11 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
* when {@link android.inputmethodservice.InputMethodService} requests to show IME
* on {@param imeTarget}.
*
- * @param imeTarget imeTarget on which IME show request is coming from.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param imeTarget imeTarget on which IME request is coming from.
+ * @param statsToken the token tracking the current IME request.
*/
void scheduleShowImePostLayout(InsetsControlTarget imeTarget,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
mImeRequester = imeTarget;
// Cancel the pre-existing stats token, if any.
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index b74eb56ebdca..cc3de7a3462c 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -60,8 +60,8 @@ interface InsetsControlTarget {
* Instructs the control target to show inset sources.
*
* @param types to specify which types of insets source window should be shown.
- * @param fromIme {@code true} if IME show request originated from {@link InputMethodService}.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param fromIme {@code true} if the IME request originated from {@link InputMethodService}.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void showInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
@@ -71,8 +71,8 @@ interface InsetsControlTarget {
* Instructs the control target to hide inset sources.
*
* @param types to specify which types of insets source window should be hidden.
- * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}.
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param fromIme {@code true} if the IME request originated from {@link InputMethodService}.
+ * @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
default void hideInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 86804360f6f4..64d8c7555fa6 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
import android.annotation.Nullable;
+import android.hardware.HardwareBuffer;
import android.util.ArrayMap;
import android.window.TaskSnapshot;
@@ -92,6 +93,10 @@ abstract class SnapshotCache<TYPE extends WindowContainer> {
if (entry != null) {
mAppIdMap.remove(entry.topApp);
mRunningCache.remove(id);
+ final HardwareBuffer buffer = entry.snapshot.getHardwareBuffer();
+ if (buffer != null) {
+ buffer.close();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 5df2edc808f6..77319cc0ba8a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -829,20 +829,20 @@ public abstract class WindowManagerInternal {
* Show IME on imeTargetWindow once IME has finished layout.
*
* @param imeTargetWindowToken token of the (IME target) window which IME should be shown.
- * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*/
public abstract void showImePostLayout(IBinder imeTargetWindowToken,
- @Nullable ImeTracker.Token statsToken);
+ @NonNull ImeTracker.Token statsToken);
/**
* Hide IME using imeTargetWindow when requested.
*
- * @param imeTargetWindowToken token of the (IME target) window on which requests hiding IME.
+ * @param imeTargetWindowToken token of the (IME target) window which requests hiding IME.
* @param displayId the id of the display the IME is on.
- * @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
+ * @param statsToken the token tracking the current IME request.
*/
public abstract void hideIme(IBinder imeTargetWindowToken, int displayId,
- @Nullable ImeTracker.Token statsToken);
+ @NonNull ImeTracker.Token statsToken);
/**
* Tell window manager about a package that should be running with a restricted range of
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7e06129832a8..ae5a5cb7316c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8258,12 +8258,17 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void showImePostLayout(IBinder imeTargetWindowToken,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
synchronized (mGlobalLock) {
InputTarget imeTarget = getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
if (imeTarget == null) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
+
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
imeTarget = controlTarget.getWindow();
@@ -8278,7 +8283,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void hideIme(IBinder imeTargetWindowToken, int displayId,
- @Nullable ImeTracker.Token statsToken) {
+ @NonNull ImeTracker.Token statsToken) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.hideIme");
synchronized (mGlobalLock) {
WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d6fc01aeadd2..a7eb444ecb59 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -329,15 +329,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
deferred);
wctApplied.meet();
if (needsSetReady) {
- // TODO(b/294925498): Remove this once we have accurate ready
- // tracking.
- if (hasActivityLaunch(wct) && !mService.mRootWindowContainer
- .allPausedActivitiesComplete()) {
- // WCT is launching an activity, so we need to wait for its
- // lifecycle events.
- return;
- }
- nextTransition.setAllReady();
+ setAllReadyIfNeeded(nextTransition, wct);
}
});
return nextTransition.getToken();
@@ -390,7 +382,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
- private static boolean hasActivityLaunch(WindowContainerTransaction wct) {
+ private static boolean hasActivityLaunch(@NonNull WindowContainerTransaction wct) {
for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
if (wct.getHierarchyOps().get(i).getType() == HIERARCHY_OP_TYPE_LAUNCH_TASK) {
return true;
@@ -399,6 +391,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return false;
}
+ private boolean isCreatedTaskFragmentReady(@NonNull WindowContainerTransaction wct) {
+ for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
+ if (op.getType() != HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION
+ || op.getTaskFragmentOperation().getOpType()
+ != OP_TYPE_CREATE_TASK_FRAGMENT) {
+ continue;
+ }
+ final IBinder tfToken = op.getTaskFragmentOperation()
+ .getTaskFragmentCreationParams().getFragmentToken();
+ final TaskFragment taskFragment = getTaskFragment(tfToken);
+ if (taskFragment != null && !taskFragment.isReadyToTransit()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void setAllReadyIfNeeded(@NonNull Transition transition,
+ @NonNull WindowContainerTransaction wct) {
+ // TODO(b/294925498): Remove this once we have accurate ready tracking.
+ if (hasActivityLaunch(wct) && !mService.mRootWindowContainer
+ .allPausedActivitiesComplete()) {
+ // WCT is launching an activity, so we need to wait for its
+ // lifecycle events.
+ return;
+ }
+ if (!isCreatedTaskFragmentReady(wct)) {
+ // When the organizer intercepts a #startActivity, it will create an empty TaskFragment
+ // for that specific incoming starting activity. We don't want to set all ready here,
+ // because we requires that #startActivity to be included in this transition, and NOT be
+ // in its own transition.
+ // TODO(b/232042367): explicitly ensure the #startActivity and this transaction are in
+ // the same transition instead of relying on this possible racing condition.
+ return;
+ }
+
+ transition.setAllReady();
+ }
+
@Override
public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
@NonNull IWindowContainerTransactionCallback callback,
@@ -529,7 +561,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
mTransitionController.requestStartTransition(transition, null /* startTask */,
remoteTransition, null /* displayChange */);
- transition.setAllReady();
+ setAllReadyIfNeeded(transition, wct);
};
mTransitionController.startCollectOrQueue(transition, doApply);
} finally {
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 1c71a6287c79..1d225ba09bbd 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -30,7 +30,10 @@ import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SH
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT;
import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.any;
+import static org.mockito.AdditionalMatchers.and;
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -40,6 +43,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Display;
+import android.view.inputmethod.ImeTracker;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -56,7 +60,7 @@ import org.junit.runner.RunWith;
* Test the behavior of {@link DefaultImeVisibilityApplier} when performing or applying the IME
* visibility state.
*
- * Build/Install/Run:
+ * <p>Build/Install/Run:
* atest FrameworksInputMethodSystemServerTests:DefaultImeVisibilityApplierTest
*/
@RunWith(AndroidJUnit4.class)
@@ -75,7 +79,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
public void testPerformShowIme() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.performShowIme(new Binder() /* showInputToken */,
- null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT);
+ ImeTracker.Token.empty(), 0 /* showFlags */, null /* resultReceiver */,
+ SHOW_SOFT_INPUT);
}
verifyShowSoftInput(false, true, 0 /* showFlags */);
}
@@ -84,46 +89,66 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
public void testPerformHideIme() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.performHideIme(new Binder() /* hideInputToken */,
- null /* statsToken */, null, HIDE_SOFT_INPUT);
+ ImeTracker.Token.empty(), null /* resultReceiver */, HIDE_SOFT_INPUT);
}
verifyHideSoftInput(false, true);
}
@Test
public void testApplyImeVisibility_throwForInvalidState() {
- assertThrows(IllegalArgumentException.class,
- () -> mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID));
+ assertThrows(IllegalArgumentException.class, () -> {
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_INVALID);
+ }
+ });
}
@Test
public void testApplyImeVisibility_showIme() {
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME);
- verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), any());
+ final var statsToken = ImeTracker.Token.empty();
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME);
+ }
+ verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken));
}
@Test
public void testApplyImeVisibility_hideIme() {
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME);
- verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt(), any());
+ final var statsToken = ImeTracker.Token.empty();
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+ }
+ verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */,
+ eq(statsToken));
}
@Test
public void testApplyImeVisibility_hideImeExplicit() throws Exception {
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_EXPLICIT);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_HIDE_IME_EXPLICIT);
+ }
verifyHideSoftInput(true, true);
}
@Test
public void testApplyImeVisibility_hideNotAlways() throws Exception {
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_NOT_ALWAYS);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_HIDE_IME_NOT_ALWAYS);
+ }
verifyHideSoftInput(true, true);
}
@Test
public void testApplyImeVisibility_showImeImplicit() throws Exception {
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT);
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
+ STATE_SHOW_IME_IMPLICIT);
+ }
verifyShowSoftInput(true, true, 0 /* showFlags */);
}
@@ -135,21 +160,21 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
mInputMethodManagerService.setAttachedClientForTesting(null);
startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
// Verify hideIme will apply the expected displayId when the default IME
// visibility applier app STATE_HIDE_IME.
- mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
- eq(mWindowToken), eq(displayIdToShowIme), eq(null));
+ eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken));
}
}
@Test
public void testShowImeScreenshot() {
synchronized (ImfLock.class) {
- mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY,
- null /* statsToken */);
+ mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY);
}
verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken),
@@ -174,17 +199,20 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
synchronized (ImfLock.class) {
// Simulate the system hides the IME when switching IME services in different users.
// (e.g. unbinding the IME from the current user to the profile user)
+ final var statsToken = ImeTracker.Token.empty();
final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
- mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, null, 0, null,
+ mInputMethodManagerService.hideCurrentInputLocked(mWindowToken,
+ statsToken, 0 /* flags */, null /* resultReceiver */,
HIDE_SWITCH_USER);
mInputMethodManagerService.onUnbindCurrentMethodByReset();
// Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing
// the IME hidden state.
- verify(mVisibilityApplier).applyImeVisibility(eq(mWindowToken), any(),
- eq(STATE_HIDE_IME));
+ // The unbind will cancel the previous stats token, and create a new one internally.
+ verify(mVisibilityApplier).applyImeVisibility(
+ eq(mWindowToken), any(), eq(STATE_HIDE_IME));
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
- eq(mWindowToken), eq(displayIdToShowIme), eq(null));
+ eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
}
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index fae5f86e4007..a22cacbcb5df 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -39,9 +39,12 @@ import static com.android.server.inputmethod.InputMethodManagerService.ImeDispla
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.notNull;
+
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -58,7 +61,7 @@ import org.mockito.ArgumentCaptor;
* Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when
* requesting the IME visibility.
*
- * Build/Install/Run:
+ * <p> Build/Install/Run:
* atest FrameworksInputMethodSystemServerTests:ImeVisibilityStateComputerTest
*/
@RunWith(AndroidJUnit4.class)
@@ -91,7 +94,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showImplicit() {
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+ InputMethodManager.SHOW_IMPLICIT);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -106,7 +110,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showExplicit() {
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -125,7 +129,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showExplicit_thenShowImplicit() {
initImeTargetWindowState(mWindowToken);
- mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
assertThat(mComputer.mRequestedShowExplicitly).isTrue();
mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
@@ -139,10 +143,10 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showForced_thenShowExplicit() {
initImeTargetWindowState(mWindowToken);
- mComputer.onImeShowFlags(null, InputMethodManager.SHOW_FORCED);
+ mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_FORCED);
assertThat(mComputer.mShowForced).isTrue();
- mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
assertThat(mComputer.mShowForced).isTrue();
}
@@ -152,7 +156,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN);
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+ InputMethodManager.SHOW_IMPLICIT);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -170,7 +175,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+ boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+ InputMethodManager.SHOW_IMPLICIT);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -188,7 +194,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
mComputer.setInputShown(true);
initImeTargetWindowState(mWindowToken);
- assertThat(mComputer.canHideIme(null, InputMethodManager.HIDE_NOT_ALWAYS)).isTrue();
+ assertThat(mComputer.canHideIme(ImeTracker.Token.empty(),
+ InputMethodManager.HIDE_NOT_ALWAYS)).isTrue();
mComputer.requestImeVisibility(mWindowToken, false);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -281,7 +288,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass(
ImeVisibilityResult.class);
verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(),
- resultCaptor.capture());
+ notNull() /* statsToken */, resultCaptor.capture());
final IBinder imeInputTarget = targetCaptor.getValue();
final ImeVisibilityResult result = resultCaptor.getValue();
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index a1be00aab340..f4d95afaacf4 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -277,8 +278,9 @@ public class InputMethodManagerServiceTestBase {
.setCurrentMethodVisible();
}
verify(mMockInputMethod, times(showSoftInput ? 1 : 0))
- .showSoftInput(any(), any(),
- showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt(), any());
+ .showSoftInput(any() /* showInputToken */ , notNull() /* statsToken */,
+ showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt() /* flags*/,
+ any() /* resultReceiver */);
}
protected void verifyHideSoftInput(boolean setNotVisible, boolean hideSoftInput)
@@ -288,6 +290,7 @@ public class InputMethodManagerServiceTestBase {
.setCurrentMethodNotVisible();
}
verify(mMockInputMethod, times(hideSoftInput ? 1 : 0))
- .hideSoftInput(any(), any(), anyInt(), any());
+ .hideSoftInput(any() /* hideInputToken */, notNull() /* statsToken */,
+ anyInt() /* flags */, any() /* resultReceiver */);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index fcee70fd0702..e9315c8ed8e6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1727,6 +1727,27 @@ public final class DisplayPowerControllerTest {
/* ignoreAnimationLimits= */ anyBoolean());
}
+ @Test
+ public void testDefaultDozeBrightness() {
+ float brightness = 0.121f;
+ when(mPowerManagerMock.getBrightnessConstraint(
+ PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)).thenReturn(brightness);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness), /* linearSecondTarget= */ anyFloat(),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 886780655de2..ba462e363b4e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -215,7 +215,7 @@ public class AutomaticBrightnessStrategyTest {
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
int targetDisplayState = Display.STATE_DOZE;
boolean allowAutoBrightnessWhileDozing = true;
- int brightnessReason = BrightnessReason.REASON_DOZE;
+ int brightnessReason = BrightnessReason.REASON_UNKNOWN;
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
float lastUserSetBrightness = 0.2f;
boolean userSetBrightnessChanged = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 20bb549301aa..faa6d97ce0e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue;
import android.graphics.PixelFormat;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
+import android.view.inputmethod.ImeTracker;
import androidx.test.filters.SmallTest;
@@ -34,6 +35,12 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for the {@link ImeInsetsSourceProvider} class.
+ *
+ * <p> Build/Install/Run:
+ * atest WmTests:ImeInsetsSourceProviderTest
+ */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
@@ -56,7 +63,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mDisplayContent.setImeControlTarget(popup);
mDisplayContent.setImeLayeringTarget(appWin);
popup.mAttrs.format = PixelFormat.TRANSPARENT;
- mImeProvider.scheduleShowImePostLayout(appWin, null /* statsToken */);
+ mImeProvider.scheduleShowImePostLayout(appWin, ImeTracker.Token.empty());
assertTrue(mImeProvider.isReadyToShowIme());
}
@@ -65,7 +72,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
WindowState target = createWindow(null, TYPE_APPLICATION, "app");
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.updateImeInputAndControlTarget(target);
- mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */);
+ mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
assertTrue(mImeProvider.isReadyToShowIme());
}
@@ -79,7 +86,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.setImeControlTarget(target);
- mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */);
+ mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
assertFalse(mImeProvider.isImeShowing());
mImeProvider.checkShowImePostLayout();
assertTrue(mImeProvider.isImeShowing());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index cd3ce9192509..c8ad4bd47880 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -104,6 +104,7 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
@@ -126,7 +127,7 @@ import java.util.List;
/**
* Tests for the {@link WindowState} class.
*
- * Build/Install/Run:
+ * <p> Build/Install/Run:
* atest WmTests:WindowStateTests
*/
@SmallTest
@@ -1099,7 +1100,7 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.setImeInputTarget(app);
app.setRequestedVisibleTypes(ime(), ime());
assertTrue(mDisplayContent.shouldImeAttachedToApp());
- controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */);
+ controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty());
controller.getImeSourceProvider().getSource().setVisible(true);
controller.updateAboveInsetsState(false);
@@ -1137,7 +1138,7 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.setImeInputTarget(app);
app.setRequestedVisibleTypes(ime(), ime());
assertTrue(mDisplayContent.shouldImeAttachedToApp());
- controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */);
+ controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty());
controller.getImeSourceProvider().getSource().setVisible(true);
controller.updateAboveInsetsState(false);