summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/app_process/app_main.cpp10
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/system-current.txt42
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java16
-rw-r--r--core/java/android/security/FileIntegrityManager.java70
-rw-r--r--core/java/android/security/IFileIntegrityService.aidl7
-rw-r--r--core/java/android/service/credentials/Action.java9
-rw-r--r--core/java/android/service/credentials/CreateEntry.java9
-rw-r--r--core/java/android/service/credentials/CredentialEntry.java10
-rw-r--r--core/java/android/service/credentials/RemoteEntry.java9
-rw-r--r--core/java/android/service/voice/HotwordTrainingAudio.aidl19
-rw-r--r--core/java/android/service/voice/HotwordTrainingAudio.java367
-rw-r--r--core/java/android/service/voice/HotwordTrainingData.aidl19
-rw-r--r--core/java/android/service/voice/HotwordTrainingData.java371
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java3
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java4
-rw-r--r--core/jni/android_util_Process.cpp2
-rw-r--r--core/res/res/values/colors.xml4
-rw-r--r--core/res/res/values/config.xml15
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java194
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt85
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS5
-rw-r--r--packages/SystemUI/Android.bp11
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt58
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt92
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt17
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconView.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorActual.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEvent.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventProducer.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt233
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt15
-rw-r--r--packages/services/VirtualCamera/OWNERS3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java8
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java17
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java66
-rw-r--r--services/core/java/com/android/server/appop/TEST_MAPPING4
-rw-r--r--services/core/java/com/android/server/audio/UuidUtils.java38
-rw-r--r--services/core/java/com/android/server/biometrics/AuthenticationStats.java5
-rw-r--r--services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java11
-rw-r--r--services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java4
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java40
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java27
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java68
-rw-r--r--services/core/java/com/android/server/pm/Installer.java50
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java56
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/TEST_MAPPING4
-rw-r--r--services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java6
-rw-r--r--services/core/java/com/android/server/security/FileIntegrityService.java51
-rw-r--r--services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java68
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java16
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java27
-rw-r--r--services/core/java/com/android/server/wm/Task.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java16
-rw-r--r--services/net/java/android/net/util/NetworkConstants.java1
-rw-r--r--services/permission/TEST_MAPPING4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java312
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java25
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java33
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java20
181 files changed, 4372 insertions, 1566 deletions
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 28db61f7d98a..66248128ff29 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -49,7 +49,7 @@ public:
virtual void onVmCreated(JNIEnv* env)
{
- if (mClassName.isEmpty()) {
+ if (mClassName.empty()) {
return; // Zygote. Nothing to do here.
}
@@ -98,7 +98,7 @@ public:
virtual void onExit(int code)
{
- if (mClassName.isEmpty()) {
+ if (mClassName.empty()) {
// if zygote
IPCThreadState::self()->stopProcess();
hardware::IPCThreadState::self()->stopProcess();
@@ -282,7 +282,7 @@ int main(int argc, char* const argv[])
}
Vector<String8> args;
- if (!className.isEmpty()) {
+ if (!className.empty()) {
// We're not in zygote mode, the only argument we need to pass
// to RuntimeInit is the application argument.
//
@@ -328,13 +328,13 @@ int main(int argc, char* const argv[])
}
}
- if (!niceName.isEmpty()) {
+ if (!niceName.empty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
- } else if (!className.isEmpty()) {
+ } else if (!className.empty()) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
diff --git a/core/api/current.txt b/core/api/current.txt
index d26cf2e60121..e00362407fc7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -39351,8 +39351,10 @@ package android.security {
}
public final class FileIntegrityManager {
+ method @FlaggedApi(Flags.FLAG_FSVERITY_API) @Nullable public byte[] getFsverityDigest(@NonNull java.io.File) throws java.io.IOException;
method public boolean isApkVeritySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public boolean isAppSourceCertificateTrusted(@NonNull java.security.cert.X509Certificate) throws java.security.cert.CertificateEncodingException;
+ method @FlaggedApi(Flags.FLAG_FSVERITY_API) public void setupFsverity(@NonNull java.io.File) throws java.io.IOException;
}
public final class KeyChain {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 427516735f95..adbd06c10f47 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13262,6 +13262,48 @@ package android.service.voice {
method @NonNull public android.service.voice.HotwordRejectedResult.Builder setConfidenceLevel(int);
}
+ public final class HotwordTrainingAudio implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.media.AudioFormat getAudioFormat();
+ method @NonNull public int getAudioType();
+ method @NonNull public byte[] getHotwordAudio();
+ method public int getHotwordOffsetMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingAudio> CREATOR;
+ field public static final int HOTWORD_OFFSET_UNSET = -1; // 0xffffffff
+ }
+
+ public static final class HotwordTrainingAudio.Builder {
+ ctor public HotwordTrainingAudio.Builder(@NonNull byte[], @NonNull android.media.AudioFormat);
+ method @NonNull public android.service.voice.HotwordTrainingAudio build();
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioType(@NonNull int);
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(@NonNull byte...);
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordOffsetMillis(int);
+ }
+
+ public final class HotwordTrainingData implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getMaxTrainingDataSize();
+ method public int getTimeoutStage();
+ method @NonNull public java.util.List<android.service.voice.HotwordTrainingAudio> getTrainingAudios();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingData> CREATOR;
+ field public static final int TIMEOUT_STAGE_EARLY = 2; // 0x2
+ field public static final int TIMEOUT_STAGE_LATE = 4; // 0x4
+ field public static final int TIMEOUT_STAGE_MIDDLE = 3; // 0x3
+ field public static final int TIMEOUT_STAGE_UNKNOWN = 0; // 0x0
+ field public static final int TIMEOUT_STAGE_VERY_EARLY = 1; // 0x1
+ }
+
+ public static final class HotwordTrainingData.Builder {
+ ctor public HotwordTrainingData.Builder();
+ method @NonNull public android.service.voice.HotwordTrainingData.Builder addTrainingAudio(@NonNull android.service.voice.HotwordTrainingAudio);
+ method @NonNull public android.service.voice.HotwordTrainingData build();
+ method @NonNull public android.service.voice.HotwordTrainingData.Builder setTimeoutStage(int);
+ method @NonNull public android.service.voice.HotwordTrainingData.Builder setTrainingAudios(@NonNull java.util.List<android.service.voice.HotwordTrainingAudio>);
+ }
+
public interface SandboxedDetectionInitializer {
method public static int getMaxCustomInitializationStatus();
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 22e8251b3f52..8961846728a6 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -20,8 +20,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.UserInfo;
+import android.os.IInstalld;
import android.os.IVold;
+import android.os.ParcelFileDescriptor;
+import java.io.IOException;
import java.util.List;
import java.util.Set;
@@ -185,4 +188,17 @@ public abstract class StorageManagerInternal {
public abstract void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid,
List<UserInfo> users);
+ /**
+ * A proxy call to the corresponding method in Installer.
+ * @see com.android.server.pm.Installer#createFsveritySetupAuthToken()
+ */
+ public abstract IInstalld.IFsveritySetupAuthToken createFsveritySetupAuthToken(
+ ParcelFileDescriptor authFd, int appUid, @UserIdInt int userId) throws IOException;
+
+ /**
+ * A proxy call to the corresponding method in Installer.
+ * @see com.android.server.pm.Installer#enableFsverity()
+ */
+ public abstract int enableFsverity(IInstalld.IFsveritySetupAuthToken authToken, String filePath,
+ String packageName) throws IOException;
}
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 266046e57cd5..7869404c265f 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -16,12 +16,21 @@
package android.security;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.os.IInstalld.IFsveritySetupAuthToken;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.system.ErrnoException;
+import com.android.internal.security.VerityUtils;
+
+import java.io.File;
+import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
@@ -55,6 +64,67 @@ public final class FileIntegrityManager {
}
/**
+ * Enables fs-verity to the owned file under the calling app's private directory. It always uses
+ * the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt.
+ *
+ * The operation can only succeed when the file is not opened as writable by any process.
+ *
+ * It takes O(file size) time to build the underlying data structure for continuous
+ * verification. The operation is atomic, i.e. it's either enabled or not, even in case of
+ * power failure during or after the call.
+ *
+ * Note for the API users: When the file's authenticity is crucial, the app typical needs to
+ * perform a signature check by itself before using the file. The signature is often delivered
+ * as a separate file and stored next to the targeting file in the filesystem. The public key of
+ * the signer (normally the same app developer) can be put in the APK, and the app can use the
+ * public key to verify the signature to the file's actual fs-verity digest (from {@link
+ * #getFsverityDigest}) before using the file. The exact format is not prescribed by the
+ * framework. App developers may choose to use common practices like JCA for the signing and
+ * verification, or their own preferred approach.
+ *
+ * @param file The file to enable fs-verity. It should be an absolute path.
+ *
+ * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+ */
+ @FlaggedApi(Flags.FLAG_FSVERITY_API)
+ public void setupFsverity(@NonNull File file) throws IOException {
+ if (!file.isAbsolute()) {
+ throw new IllegalArgumentException("Expect an absolute path");
+ }
+ IFsveritySetupAuthToken authToken;
+ // fs-verity setup requires no writable fd to the file. Make sure it's closed before
+ // continue.
+ try (var authFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)) {
+ authToken = mService.createAuthToken(authFd);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ try {
+ int errno = mService.setupFsverity(authToken, file.getPath(),
+ mContext.getPackageName());
+ if (errno != 0) {
+ new ErrnoException("setupFsverity", errno).rethrowAsIOException();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the fs-verity digest for the owned file under the calling app's
+ * private directory, or null when the file does not have fs-verity enabled.
+ *
+ * @param file The file to measure the fs-verity digest.
+ * @return The fs-verity digeset in byte[], null if none.
+ * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+ */
+ @FlaggedApi(Flags.FLAG_FSVERITY_API)
+ public @Nullable byte[] getFsverityDigest(@NonNull File file) throws IOException {
+ return VerityUtils.getFsverityDigest(file.getPath());
+ }
+
+ /**
* Returns whether the given certificate can be used to prove app's install source. Always
* return false if the feature is not supported.
*
diff --git a/core/java/android/security/IFileIntegrityService.aidl b/core/java/android/security/IFileIntegrityService.aidl
index dff347ec758a..1a6cf881e337 100644
--- a/core/java/android/security/IFileIntegrityService.aidl
+++ b/core/java/android/security/IFileIntegrityService.aidl
@@ -16,6 +16,9 @@
package android.security;
+import android.os.ParcelFileDescriptor;
+import android.os.IInstalld;
+
/**
* Binder interface to communicate with FileIntegrityService.
* @hide
@@ -23,4 +26,8 @@ package android.security;
interface IFileIntegrityService {
boolean isApkVeritySupported();
boolean isAppSourceCertificateTrusted(in byte[] certificateBytes, in String packageName);
+
+ IInstalld.IFsveritySetupAuthToken createAuthToken(in ParcelFileDescriptor authFd);
+ int setupFsverity(IInstalld.IFsveritySetupAuthToken authToken, in String filePath,
+ in String packageName);
}
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
index 55133aea4fd6..9b1132a872c1 100644
--- a/core/java/android/service/credentials/Action.java
+++ b/core/java/android/service/credentials/Action.java
@@ -46,7 +46,14 @@ public final class Action implements Parcelable {
* <p> See details on usage of {@code Action} for various actionable entries in
* {@link BeginCreateCredentialResponse} and {@link BeginGetCredentialResponse}.
*
- * @param slice the display content to be displayed on the UI, along with this action
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.Action} class populated as slice items
+ * against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*/
public Action(@NonNull Slice slice) {
Objects.requireNonNull(slice, "slice must not be null");
diff --git a/core/java/android/service/credentials/CreateEntry.java b/core/java/android/service/credentials/CreateEntry.java
index 6a9f09f257f3..2495c7dd9f25 100644
--- a/core/java/android/service/credentials/CreateEntry.java
+++ b/core/java/android/service/credentials/CreateEntry.java
@@ -66,7 +66,14 @@ public final class CreateEntry implements Parcelable {
/**
* Constructs a CreateEntry to be displayed on the UI.
*
- * @param slice the display content to be displayed on the UI, along with this entry
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.CreateEntry} class populated as slice items
+ * against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*/
public CreateEntry(
@NonNull Slice slice) {
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index 512d8339e944..53094e800cf6 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -69,8 +69,14 @@ public final class CredentialEntry implements Parcelable {
* receive the complete corresponding
* {@link GetCredentialRequest}.
* @param type the type of the credential for which this credential entry is being created
- * @param slice the slice containing the metadata to be shown on the UI. Must be
- * constructed through the androidx.credentials jetpack library.
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.CredentialEntry} class populated as slice
+ * items against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*
* @throws IllegalArgumentException If {@code beginGetCredentialOptionId} or {@code type}
* is null, or empty
diff --git a/core/java/android/service/credentials/RemoteEntry.java b/core/java/android/service/credentials/RemoteEntry.java
index 5b3218ad9aa8..5fd9925d84c9 100644
--- a/core/java/android/service/credentials/RemoteEntry.java
+++ b/core/java/android/service/credentials/RemoteEntry.java
@@ -73,7 +73,14 @@ public final class RemoteEntry implements Parcelable {
/**
* Constructs a RemoteEntry to be displayed on the UI.
*
- * @param slice the display content to be displayed on the UI, along with this entry
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.RemoteEntry} class populated as slice items
+ * against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*/
public RemoteEntry(
@NonNull Slice slice) {
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/core/java/android/service/voice/HotwordTrainingAudio.aidl
new file mode 100644
index 000000000000..4dd22897f831
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingAudio.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+parcelable HotwordTrainingAudio; \ No newline at end of file
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.java b/core/java/android/service/voice/HotwordTrainingAudio.java
new file mode 100644
index 000000000000..895b0c0e2d55
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingAudio.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.media.AudioFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Represents audio supporting hotword model training.
+ *
+ * @hide
+ */
+@DataClass(
+ genConstructor = false,
+ genBuilder = true,
+ genEqualsHashCode = true,
+ genHiddenConstDefs = true,
+ genParcelable = true,
+ genToString = true
+)
+@SystemApi
+public final class HotwordTrainingAudio implements Parcelable {
+ /** Represents unset value for the hotword offset. */
+ public static final int HOTWORD_OFFSET_UNSET = -1;
+
+ /** Buffer of hotword audio data for training models. */
+ @NonNull
+ private final byte[] mHotwordAudio;
+
+ private String hotwordAudioToString() {
+ return "length=" + mHotwordAudio.length;
+ }
+
+ /**
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ @NonNull
+ private final AudioFormat mAudioFormat;
+
+ /**
+ * App-defined identifier to distinguish hotword training audio instances.
+ */
+ @NonNull
+ private final int mAudioType;
+
+ private static int defaultAudioType() {
+ return 0;
+ }
+
+ /**
+ * App-defined offset in milliseconds relative to start of
+ * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
+ * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
+ */
+ private int mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingAudio(
+ @NonNull byte[] hotwordAudio,
+ @NonNull AudioFormat audioFormat,
+ @NonNull int audioType,
+ int hotwordOffsetMillis) {
+ this.mHotwordAudio = hotwordAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHotwordAudio);
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioType = audioType;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioType);
+ this.mHotwordOffsetMillis = hotwordOffsetMillis;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * Buffer of hotword audio data for training models.
+ */
+ @DataClass.Generated.Member
+ public @NonNull byte[] getHotwordAudio() {
+ return mHotwordAudio;
+ }
+
+ /**
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull AudioFormat getAudioFormat() {
+ return mAudioFormat;
+ }
+
+ /**
+ * App-defined identifier to distinguish hotword training audio instances.
+ */
+ @DataClass.Generated.Member
+ public @NonNull int getAudioType() {
+ return mAudioType;
+ }
+
+ /**
+ * App-defined offset in milliseconds relative to start of
+ * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
+ * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
+ */
+ @DataClass.Generated.Member
+ public int getHotwordOffsetMillis() {
+ return mHotwordOffsetMillis;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "HotwordTrainingAudio { " +
+ "hotwordAudio = " + hotwordAudioToString() + ", " +
+ "audioFormat = " + mAudioFormat + ", " +
+ "audioType = " + mAudioType + ", " +
+ "hotwordOffsetMillis = " + mHotwordOffsetMillis +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(HotwordTrainingAudio other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ HotwordTrainingAudio that = (HotwordTrainingAudio) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Arrays.equals(mHotwordAudio, that.mHotwordAudio)
+ && java.util.Objects.equals(mAudioFormat, that.mAudioFormat)
+ && mAudioType == that.mAudioType
+ && mHotwordOffsetMillis == that.mHotwordOffsetMillis;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Arrays.hashCode(mHotwordAudio);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mAudioFormat);
+ _hash = 31 * _hash + mAudioType;
+ _hash = 31 * _hash + mHotwordOffsetMillis;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeByteArray(mHotwordAudio);
+ dest.writeTypedObject(mAudioFormat, flags);
+ dest.writeInt(mAudioType);
+ dest.writeInt(mHotwordOffsetMillis);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingAudio(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte[] hotwordAudio = in.createByteArray();
+ AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR);
+ int audioType = in.readInt();
+ int hotwordOffsetMillis = in.readInt();
+
+ this.mHotwordAudio = hotwordAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHotwordAudio);
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioType = audioType;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioType);
+ this.mHotwordOffsetMillis = hotwordOffsetMillis;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<HotwordTrainingAudio> CREATOR
+ = new Parcelable.Creator<HotwordTrainingAudio>() {
+ @Override
+ public HotwordTrainingAudio[] newArray(int size) {
+ return new HotwordTrainingAudio[size];
+ }
+
+ @Override
+ public HotwordTrainingAudio createFromParcel(@NonNull Parcel in) {
+ return new HotwordTrainingAudio(in);
+ }
+ };
+
+ /**
+ * A builder for {@link HotwordTrainingAudio}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @NonNull byte[] mHotwordAudio;
+ private @NonNull AudioFormat mAudioFormat;
+ private @NonNull int mAudioType;
+ private int mHotwordOffsetMillis;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param hotwordAudio
+ * Buffer of hotword audio data for training models.
+ * @param audioFormat
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ public Builder(
+ @NonNull byte[] hotwordAudio,
+ @NonNull AudioFormat audioFormat) {
+ mHotwordAudio = hotwordAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHotwordAudio);
+ mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ }
+
+ /**
+ * Buffer of hotword audio data for training models.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setHotwordAudio(@NonNull byte... value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mHotwordAudio = value;
+ return this;
+ }
+
+ /**
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAudioFormat(@NonNull AudioFormat value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mAudioFormat = value;
+ return this;
+ }
+
+ /**
+ * App-defined identifier to distinguish hotword training audio instances.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAudioType(@NonNull int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mAudioType = value;
+ return this;
+ }
+
+ /**
+ * App-defined offset in milliseconds relative to start of
+ * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
+ * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setHotwordOffsetMillis(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mHotwordOffsetMillis = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull HotwordTrainingAudio build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mAudioType = defaultAudioType();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
+ }
+ HotwordTrainingAudio o = new HotwordTrainingAudio(
+ mHotwordAudio,
+ mAudioFormat,
+ mAudioType,
+ mHotwordOffsetMillis);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1692837160437L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java",
+ inputSignatures = "public static final int HOTWORD_OFFSET_UNSET\nprivate final @android.annotation.NonNull byte[] mHotwordAudio\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull int mAudioType\nprivate int mHotwordOffsetMillis\nprivate java.lang.String hotwordAudioToString()\nprivate static int defaultAudioType()\nclass HotwordTrainingAudio extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/voice/HotwordTrainingData.aidl b/core/java/android/service/voice/HotwordTrainingData.aidl
new file mode 100644
index 000000000000..03cc8413d780
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingData.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+parcelable HotwordTrainingData;
diff --git a/core/java/android/service/voice/HotwordTrainingData.java b/core/java/android/service/voice/HotwordTrainingData.java
new file mode 100644
index 000000000000..9dca77ed7f51
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingData.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Contains training data related to hotword detection service.
+ *
+ * <p>The constructed object's size must be within
+ * {@link HotwordTrainingData#getMaxTrainingDataSize()} or an
+ * {@link IllegalArgumentException} will be thrown on construction. Size of the object is calculated
+ * by converting object to a {@link Parcel} and using the {@link Parcel#dataSize()}.
+ *
+ * @hide
+ */
+@DataClass(
+ genConstructor = false,
+ genBuilder = true,
+ genEqualsHashCode = true,
+ genHiddenConstDefs = true,
+ genParcelable = true,
+ genToString = true)
+@SystemApi
+public final class HotwordTrainingData implements Parcelable {
+ /** Max size for hotword training data. */
+ public static int getMaxTrainingDataSize() {
+ return 1024 * 1024; // 1 MB;
+ }
+
+ /** The list containing hotword audio that is useful for training. */
+ @NonNull
+ @DataClass.PluralOf("trainingAudio")
+ private final List<HotwordTrainingAudio> mTrainingAudios;
+
+ private static List<HotwordTrainingAudio> defaultTrainingAudios() {
+ return Collections.emptyList();
+ }
+
+ /** Timeout stage is unknown. */
+ public static final int TIMEOUT_STAGE_UNKNOWN = 0;
+
+ /**
+ * Timeout stage value that represents that the model timed out very early while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_VERY_EARLY = 1;
+
+ /**
+ * Timeout stage value that represents that the model timed out early while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_EARLY = 2;
+
+ /**
+ * Timeout stage value that represents that the model timed out in the middle while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_MIDDLE = 3;
+
+ /**
+ * Timeout stage value that represents that the model timed out late while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_LATE = 4;
+
+ /** @hide */
+ @IntDef(prefix = {"TIMEOUT_STAGE"}, value = {
+ TIMEOUT_STAGE_UNKNOWN,
+ TIMEOUT_STAGE_VERY_EARLY,
+ TIMEOUT_STAGE_EARLY,
+ TIMEOUT_STAGE_MIDDLE,
+ TIMEOUT_STAGE_LATE,
+ })
+ @interface HotwordTimeoutStage {}
+
+ /** Stage when timeout occurred. */
+ @HotwordTimeoutStage
+ private final int mTimeoutStage;
+
+ private static int defaultTimeoutStage() {
+ return TIMEOUT_STAGE_UNKNOWN;
+ }
+
+ private void onConstructed() {
+ // Verify size of object is within limit.
+ Parcel parcel = Parcel.obtain();
+ parcel.writeValue(this);
+ int dataSizeBytes = parcel.dataSize();
+ parcel.recycle();
+ Preconditions.checkArgument(
+ dataSizeBytes < getMaxTrainingDataSize(),
+ TextUtils.formatSimple(
+ "Hotword training data of size %s exceeds size limit of %s!",
+ dataSizeBytes, getMaxTrainingDataSize()));
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingData.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /** @hide */
+ @IntDef(prefix = "TIMEOUT_STAGE_", value = {
+ TIMEOUT_STAGE_UNKNOWN,
+ TIMEOUT_STAGE_VERY_EARLY,
+ TIMEOUT_STAGE_EARLY,
+ TIMEOUT_STAGE_MIDDLE,
+ TIMEOUT_STAGE_LATE
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface TimeoutStage {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String timeoutStageToString(@TimeoutStage int value) {
+ switch (value) {
+ case TIMEOUT_STAGE_UNKNOWN:
+ return "TIMEOUT_STAGE_UNKNOWN";
+ case TIMEOUT_STAGE_VERY_EARLY:
+ return "TIMEOUT_STAGE_VERY_EARLY";
+ case TIMEOUT_STAGE_EARLY:
+ return "TIMEOUT_STAGE_EARLY";
+ case TIMEOUT_STAGE_MIDDLE:
+ return "TIMEOUT_STAGE_MIDDLE";
+ case TIMEOUT_STAGE_LATE:
+ return "TIMEOUT_STAGE_LATE";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingData(
+ @NonNull List<HotwordTrainingAudio> trainingAudios,
+ @HotwordTimeoutStage int timeoutStage) {
+ this.mTrainingAudios = trainingAudios;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTrainingAudios);
+ this.mTimeoutStage = timeoutStage;
+ com.android.internal.util.AnnotationValidations.validate(
+ HotwordTimeoutStage.class, null, mTimeoutStage);
+
+ onConstructed();
+ }
+
+ /**
+ * The list containing hotword audio that is useful for training.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<HotwordTrainingAudio> getTrainingAudios() {
+ return mTrainingAudios;
+ }
+
+ /**
+ * Stage when timeout occurred.
+ */
+ @DataClass.Generated.Member
+ public @HotwordTimeoutStage int getTimeoutStage() {
+ return mTimeoutStage;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "HotwordTrainingData { " +
+ "trainingAudios = " + mTrainingAudios + ", " +
+ "timeoutStage = " + mTimeoutStage +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(HotwordTrainingData other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ HotwordTrainingData that = (HotwordTrainingData) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mTrainingAudios, that.mTrainingAudios)
+ && mTimeoutStage == that.mTimeoutStage;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingAudios);
+ _hash = 31 * _hash + mTimeoutStage;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeParcelableList(mTrainingAudios, flags);
+ dest.writeInt(mTimeoutStage);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingData(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ List<HotwordTrainingAudio> trainingAudios = new ArrayList<>();
+ in.readParcelableList(trainingAudios, HotwordTrainingAudio.class.getClassLoader());
+ int timeoutStage = in.readInt();
+
+ this.mTrainingAudios = trainingAudios;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTrainingAudios);
+ this.mTimeoutStage = timeoutStage;
+ com.android.internal.util.AnnotationValidations.validate(
+ HotwordTimeoutStage.class, null, mTimeoutStage);
+
+ onConstructed();
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<HotwordTrainingData> CREATOR
+ = new Parcelable.Creator<HotwordTrainingData>() {
+ @Override
+ public HotwordTrainingData[] newArray(int size) {
+ return new HotwordTrainingData[size];
+ }
+
+ @Override
+ public HotwordTrainingData createFromParcel(@NonNull Parcel in) {
+ return new HotwordTrainingData(in);
+ }
+ };
+
+ /**
+ * A builder for {@link HotwordTrainingData}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @NonNull List<HotwordTrainingAudio> mTrainingAudios;
+ private @HotwordTimeoutStage int mTimeoutStage;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The list containing hotword audio that is useful for training.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTrainingAudios(@NonNull List<HotwordTrainingAudio> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mTrainingAudios = value;
+ return this;
+ }
+
+ /** @see #setTrainingAudios */
+ @DataClass.Generated.Member
+ public @NonNull Builder addTrainingAudio(@NonNull HotwordTrainingAudio value) {
+ if (mTrainingAudios == null) setTrainingAudios(new ArrayList<>());
+ mTrainingAudios.add(value);
+ return this;
+ }
+
+ /**
+ * Stage when timeout occurred.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTimeoutStage(@HotwordTimeoutStage int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mTimeoutStage = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull HotwordTrainingData build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mTrainingAudios = defaultTrainingAudios();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mTimeoutStage = defaultTimeoutStage();
+ }
+ HotwordTrainingData o = new HotwordTrainingData(
+ mTrainingAudios,
+ mTimeoutStage);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1693313864628L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingData.java",
+ inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"trainingAudio\") java.util.List<android.service.voice.HotwordTrainingAudio> mTrainingAudios\npublic static final int TIMEOUT_STAGE_UNKNOWN\npublic static final int TIMEOUT_STAGE_VERY_EARLY\npublic static final int TIMEOUT_STAGE_EARLY\npublic static final int TIMEOUT_STAGE_MIDDLE\npublic static final int TIMEOUT_STAGE_LATE\nprivate final @android.service.voice.HotwordTrainingData.HotwordTimeoutStage int mTimeoutStage\npublic static int getMaxTrainingDataSize()\nprivate static java.util.List<android.service.voice.HotwordTrainingAudio> defaultTrainingAudios()\nprivate static int defaultTimeoutStage()\nprivate void onConstructed()\nclass HotwordTrainingData extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 0a69ea8ce633..048912c0b2e2 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -118,9 +118,6 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String NAS_DEFAULT_SERVICE = "nas_default_service";
- /** (boolean) Whether notify() calls to NMS should acquire and hold WakeLocks. */
- public static final String NOTIFY_WAKELOCK = "nms_notify_wakelock";
-
// Flags related to media notifications
/**
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 9233050c97ad..8de448be440b 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -74,10 +74,6 @@ public class SystemUiSystemPropertiesFlags {
public static final Flag LOG_DND_STATE_EVENTS =
releasedFlag("persist.sysui.notification.log_dnd_state_events");
- /** Gating the holding of WakeLocks until NLSes are told about a new notification. */
- public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION =
- releasedFlag("persist.sysui.notification.wake_lock_for_posting_notification");
-
/** Gating storing NotificationRankingUpdate ranking map in shared memory. */
public static final Flag RANKING_UPDATE_ASHMEM = devFlag(
"persist.sysui.notification.ranking_update_ashmem");
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4f2bf4a4f6cb..b1d3d92023ad 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -583,7 +583,7 @@ void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name)
env->ReleaseStringCritical(name, str);
}
- if (!name8.isEmpty()) {
+ if (!name8.empty()) {
AndroidRuntime::getRuntime()->setArgv0(name8.string(), true /* setProcName */);
}
}
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index e67ea82cb6da..a6830a6e3793 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -552,10 +552,6 @@
<color name="accessibility_magnification_thumbnail_container_background_color">#99000000</color>
<color name="accessibility_magnification_thumbnail_container_stroke_color">#FFFFFF</color>
- <!-- Color of camera light when camera is in use -->
- <color name="camera_privacy_light_day">#FFFFFF</color>
- <color name="camera_privacy_light_night">#FFFFFF</color>
-
<!-- Lily Language Picker language item view colors -->
<color name="language_picker_item_text_color">#202124</color>
<color name="language_picker_item_text_color_secondary">#5F6368</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0ea5b8a6fa06..9e24a7320f6f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6534,8 +6534,19 @@
<!-- Interval in milliseconds to average light sensor values for camera light brightness -->
<integer name="config_cameraPrivacyLightAlsAveragingIntervalMillis">3000</integer>
- <!-- Light sensor's lux value to use as the threshold between using day or night brightness -->
- <integer name="config_cameraPrivacyLightAlsNightThreshold">4</integer>
+ <!-- Ambient Light sensor's lux values to use as the threshold between brightness colors defined
+ by config_cameraPrivacyLightColors. If the ambient brightness less than the first element
+ in this array then lights of type "camera" will be set to the color in position 0 of
+ config_cameraPrivacyLightColors. This array must be strictly increasing and have a length
+ of zero means there is only one brightness -->
+ <integer-array name="config_cameraPrivacyLightAlsLuxThresholds">
+ </integer-array>
+ <!-- Colors to configure the camera privacy light at different brightnesses. This array must
+ have exactly one more entry than config_cameraPrivacyLightAlsLuxThresholds,
+ or a length of zero if the feature isn't supported. If nonempty and the device doesn't have
+ an ambient light sensor the last element in this array will be the only one used -->
+ <array name="config_cameraPrivacyLightColors">
+ </array>
<!-- List of system components which are allowed to receive ServiceState entries in an
un-sanitized form, even if the location toggle is off. This is intended ONLY for system
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 889901af8fd4..e2fc837b9a64 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5009,10 +5009,9 @@
<java-symbol type="string" name="vdm_camera_access_denied" />
<java-symbol type="string" name="vdm_secure_window" />
- <java-symbol type="color" name="camera_privacy_light_day"/>
- <java-symbol type="color" name="camera_privacy_light_night"/>
<java-symbol type="integer" name="config_cameraPrivacyLightAlsAveragingIntervalMillis"/>
- <java-symbol type="integer" name="config_cameraPrivacyLightAlsNightThreshold"/>
+ <java-symbol type="array" name="config_cameraPrivacyLightAlsLuxThresholds"/>
+ <java-symbol type="array" name="config_cameraPrivacyLightColors"/>
<java-symbol type="bool" name="config_bg_current_drain_monitor_enabled" />
<java-symbol type="array" name="config_bg_current_drain_threshold_to_restricted_bucket" />
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d0c0c0296935..7edf2fc84f48 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -450,6 +450,10 @@
<dimen name="freeform_resize_corner">44dp</dimen>
+ <!-- The width of the area at the sides of the screen where a freeform task will transition to
+ split select if dragged until the touch input is within the range. -->
+ <dimen name="desktop_mode_transition_area_width">32dp</dimen>
+
<!-- The height of the area at the top of the screen where a freeform task will transition to
fullscreen if dragged until the top bound of the task is within the area. -->
<dimen name="desktop_mode_transition_area_height">16dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 0f0d572f8eae..a587bed3fef0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -27,6 +27,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
@@ -47,6 +48,15 @@ import com.android.wm.shell.common.SyncTransactionQueue;
* Animated visual indicator for Desktop Mode windowing transitions.
*/
public class DesktopModeVisualIndicator {
+ public static final int INVALID_INDICATOR = -1;
+ /** Indicates impending transition into desktop mode */
+ public static final int TO_DESKTOP_INDICATOR = 1;
+ /** Indicates impending transition into fullscreen */
+ public static final int TO_FULLSCREEN_INDICATOR = 2;
+ /** Indicates impending transition into split select on the left side */
+ public static final int TO_SPLIT_LEFT_INDICATOR = 3;
+ /** Indicates impending transition into split select on the right side */
+ public static final int TO_SPLIT_RIGHT_INDICATOR = 4;
private final Context mContext;
private final DisplayController mDisplayController;
@@ -54,6 +64,7 @@ public class DesktopModeVisualIndicator {
private final RootTaskDisplayAreaOrganizer mRootTdaOrganizer;
private final ActivityManager.RunningTaskInfo mTaskInfo;
private final SurfaceControl mTaskSurface;
+ private final Rect mIndicatorRange = new Rect();
private SurfaceControl mLeash;
private final SyncTransactionQueue mSyncQueue;
@@ -61,11 +72,12 @@ public class DesktopModeVisualIndicator {
private View mView;
private boolean mIsFullscreen;
+ private int mType;
public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue,
ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController,
Context context, SurfaceControl taskSurface, ShellTaskOrganizer taskOrganizer,
- RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, int type) {
mSyncQueue = syncQueue;
mTaskInfo = taskInfo;
mDisplayController = displayController;
@@ -73,10 +85,64 @@ public class DesktopModeVisualIndicator {
mTaskSurface = taskSurface;
mTaskOrganizer = taskOrganizer;
mRootTdaOrganizer = taskDisplayAreaOrganizer;
+ mType = type;
+ defineIndicatorRange();
createView();
}
/**
+ * If an indicator is warranted based on the input and task bounds, return the type of
+ * indicator that should be created.
+ */
+ public static int determineIndicatorType(PointF inputCoordinates, Rect taskBounds,
+ DisplayLayout layout, Context context) {
+ int transitionAreaHeight = context.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ int transitionAreaWidth = context.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width);
+ if (taskBounds.top <= transitionAreaHeight) return TO_FULLSCREEN_INDICATOR;
+ if (inputCoordinates.x <= transitionAreaWidth) return TO_SPLIT_LEFT_INDICATOR;
+ if (inputCoordinates.x >= layout.width() - transitionAreaWidth) {
+ return TO_SPLIT_RIGHT_INDICATOR;
+ }
+ return INVALID_INDICATOR;
+ }
+
+ /**
+ * Determine range of inputs that will keep this indicator displaying.
+ */
+ private void defineIndicatorRange() {
+ DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ int captionHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.freeform_decor_caption_height);
+ int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width);
+ switch (mType) {
+ case TO_DESKTOP_INDICATOR:
+ // TO_DESKTOP indicator is only dismissed on release; entire display is valid.
+ mIndicatorRange.set(0, 0, layout.width(), layout.height());
+ break;
+ case TO_FULLSCREEN_INDICATOR:
+ // If drag results in caption going above the top edge of the display, we still
+ // want to transition to fullscreen.
+ mIndicatorRange.set(0, -captionHeight, layout.width(), transitionAreaHeight);
+ break;
+ case TO_SPLIT_LEFT_INDICATOR:
+ mIndicatorRange.set(0, transitionAreaHeight, transitionAreaWidth, layout.height());
+ break;
+ case TO_SPLIT_RIGHT_INDICATOR:
+ mIndicatorRange.set(layout.width() - transitionAreaWidth, transitionAreaHeight,
+ layout.width(), layout.height());
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ /**
* Create a fullscreen indicator with no animation
*/
private void createView() {
@@ -85,11 +151,30 @@ public class DesktopModeVisualIndicator {
final DisplayMetrics metrics = resources.getDisplayMetrics();
final int screenWidth = metrics.widthPixels;
final int screenHeight = metrics.heightPixels;
+
mView = new View(mContext);
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder);
+ String description;
+ switch (mType) {
+ case TO_DESKTOP_INDICATOR:
+ description = "Desktop indicator";
+ break;
+ case TO_FULLSCREEN_INDICATOR:
+ description = "Fullscreen indicator";
+ break;
+ case TO_SPLIT_LEFT_INDICATOR:
+ description = "Split Left indicator";
+ break;
+ case TO_SPLIT_RIGHT_INDICATOR:
+ description = "Split Right indicator";
+ break;
+ default:
+ description = "Invalid indicator";
+ break;
+ }
mLeash = builder
- .setName("Fullscreen Indicator")
+ .setName(description)
.setContainerLayer()
.build();
t.show(mLeash);
@@ -97,14 +182,14 @@ public class DesktopModeVisualIndicator {
new WindowManager.LayoutParams(screenWidth, screenHeight,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
- lp.setTitle("Fullscreen indicator for Task=" + mTaskInfo.taskId);
+ lp.setTitle(description + " for Task=" + mTaskInfo.taskId);
lp.setTrustedOverlay();
final WindowlessWindowManager windowManager = new WindowlessWindowManager(
mTaskInfo.configuration, mLeash,
null /* hostInputToken */);
mViewHost = new SurfaceControlViewHost(mContext,
mDisplayController.getDisplay(mTaskInfo.displayId), windowManager,
- "FullscreenVisualIndicator");
+ "DesktopModeVisualIndicator");
mViewHost.setView(mView, lp);
// We want this indicator to be behind the dragged task, but in front of all others.
t.setRelativeLayer(mLeash, mTaskSurface, -1);
@@ -116,24 +201,13 @@ public class DesktopModeVisualIndicator {
}
/**
- * Create fullscreen indicator and fades it in.
- */
- public void createFullscreenIndicator() {
- mIsFullscreen = true;
- mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
- final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimator(
- mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
- animator.start();
- }
-
- /**
- * Create a fullscreen indicator. Animator fades it in while expanding the bounds outwards.
+ * Create an indicator. Animator fades it in while expanding the bounds outwards.
*/
- public void createFullscreenIndicatorWithAnimatedBounds() {
- mIsFullscreen = true;
+ public void createIndicatorWithAnimatedBounds() {
+ mIsFullscreen = mType == TO_FULLSCREEN_INDICATOR;
mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
final VisualIndicatorAnimator animator = VisualIndicatorAnimator
- .toFullscreenAnimatorWithAnimatedBounds(mView,
+ .animateBounds(mView, mType,
mDisplayController.getDisplayLayout(mTaskInfo.displayId));
animator.start();
}
@@ -143,6 +217,7 @@ public class DesktopModeVisualIndicator {
*/
public void transitionFullscreenIndicatorToFreeform() {
mIsFullscreen = false;
+ mType = TO_DESKTOP_INDICATOR;
final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFreeformAnimator(
mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
animator.start();
@@ -153,6 +228,7 @@ public class DesktopModeVisualIndicator {
*/
public void transitionFreeformIndicatorToFullscreen() {
mIsFullscreen = true;
+ mType = TO_FULLSCREEN_INDICATOR;
final VisualIndicatorAnimator animator =
VisualIndicatorAnimator.toFullscreenAnimatorWithAnimatedBounds(
mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
@@ -160,6 +236,14 @@ public class DesktopModeVisualIndicator {
}
/**
+ * Determine if a MotionEvent is in the same range that enabled the indicator.
+ * Used to dismiss the indicator when a transition will no longer result from releasing.
+ */
+ public boolean eventOutsideRange(float x, float y) {
+ return !mIndicatorRange.contains((int) x, (int) y);
+ }
+
+ /**
* Release the indicator and its components when it is no longer needed.
*/
public void releaseVisualIndicator(SurfaceControl.Transaction t) {
@@ -210,32 +294,45 @@ public class DesktopModeVisualIndicator {
* @param view the view for this indicator
* @param displayLayout information about the display the transitioning task is currently on
*/
- public static VisualIndicatorAnimator toFullscreenAnimator(@NonNull View view,
- @NonNull DisplayLayout displayLayout) {
- final Rect bounds = getMaxBounds(displayLayout);
+ public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds(
+ @NonNull View view, @NonNull DisplayLayout displayLayout) {
+ final int padding = displayLayout.stableInsets().top;
+ Rect startBounds = new Rect(padding, padding,
+ displayLayout.width() - padding, displayLayout.height() - padding);
+ view.getBackground().setBounds(startBounds);
+
final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, bounds, bounds);
+ view, startBounds, getMaxBounds(startBounds));
animator.setInterpolator(new DecelerateInterpolator());
setupIndicatorAnimation(animator);
return animator;
}
-
- /**
- * Create animator for visual indicator of fullscreen transition
- *
- * @param view the view for this indicator
- * @param displayLayout information about the display the transitioning task is currently on
- */
- public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds(
- @NonNull View view, @NonNull DisplayLayout displayLayout) {
+ public static VisualIndicatorAnimator animateBounds(
+ @NonNull View view, int type, @NonNull DisplayLayout displayLayout) {
final int padding = displayLayout.stableInsets().top;
- Rect startBounds = new Rect(padding, padding,
- displayLayout.width() - padding, displayLayout.height() - padding);
+ Rect startBounds = new Rect();
+ switch (type) {
+ case TO_FULLSCREEN_INDICATOR:
+ startBounds.set(padding, padding,
+ displayLayout.width() - padding,
+ displayLayout.height() - padding);
+ break;
+ case TO_SPLIT_LEFT_INDICATOR:
+ startBounds.set(padding, padding,
+ displayLayout.width() / 2 - padding,
+ displayLayout.height() - padding);
+ break;
+ case TO_SPLIT_RIGHT_INDICATOR:
+ startBounds.set(displayLayout.width() / 2 + padding, padding,
+ displayLayout.width() - padding,
+ displayLayout.height() - padding);
+ break;
+ }
view.getBackground().setBounds(startBounds);
final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, startBounds, getMaxBounds(displayLayout));
+ view, startBounds, getMaxBounds(startBounds));
animator.setInterpolator(new DecelerateInterpolator());
setupIndicatorAnimation(animator);
return animator;
@@ -252,12 +349,13 @@ public class DesktopModeVisualIndicator {
final float adjustmentPercentage = 1f - FINAL_FREEFORM_SCALE;
final int width = displayLayout.width();
final int height = displayLayout.height();
+ Rect startBounds = new Rect(0, 0, width, height);
Rect endBounds = new Rect((int) (adjustmentPercentage * width / 2),
(int) (adjustmentPercentage * height / 2),
(int) (displayLayout.width() - (adjustmentPercentage * width / 2)),
(int) (displayLayout.height() - (adjustmentPercentage * height / 2)));
final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, getMaxBounds(displayLayout), endBounds);
+ view, startBounds, endBounds);
animator.setInterpolator(new DecelerateInterpolator());
setupIndicatorAnimation(animator);
return animator;
@@ -310,21 +408,17 @@ public class DesktopModeVisualIndicator {
}
/**
- * Return the max bounds of a fullscreen indicator
+ * Return the max bounds of a visual indicator
*/
- private static Rect getMaxBounds(@NonNull DisplayLayout displayLayout) {
- final int padding = displayLayout.stableInsets().top;
- final int width = displayLayout.width() - 2 * padding;
- final int height = displayLayout.height() - 2 * padding;
- Rect endBounds = new Rect((int) (padding
- - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
- (int) (padding
- - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)),
- (int) (displayLayout.width() - padding
- + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
- (int) (displayLayout.height() - padding
- + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)));
- return endBounds;
+ private static Rect getMaxBounds(Rect startBounds) {
+ return new Rect((int) (startBounds.left
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
+ (int) (startBounds.top
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())),
+ (int) (startBounds.right
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
+ (int) (startBounds.bottom
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())));
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 4740a9d2e030..236dec0f555b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -29,6 +29,7 @@ import android.app.WindowConfiguration.WindowingMode
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Point
+import android.graphics.PointF
import android.graphics.Rect
import android.graphics.Region
import android.os.IBinder
@@ -55,7 +56,10 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.annotations.ExternalThread
import com.android.wm.shell.common.annotations.ShellMainThread
+import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
+import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
@@ -108,6 +112,11 @@ class DesktopTasksController(
com.android.wm.shell.R.dimen.desktop_mode_transition_area_height
)
+ private val transitionAreaWidth
+ get() = context.resources.getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
+ )
+
// This is public to avoid cyclic dependency; it is set by SplitScreenController
lateinit var splitScreenController: SplitScreenController
@@ -805,7 +814,8 @@ class DesktopTasksController(
) {
val wct = WindowContainerTransaction()
addMoveToSplitChanges(wct, taskInfo)
- splitScreenController.requestEnterSplitSelect(taskInfo, wct)
+ splitScreenController.requestEnterSplitSelect(taskInfo, wct,
+ SPLIT_POSITION_BOTTOM_OR_RIGHT, taskInfo.configuration.windowConfiguration.bounds)
}
}
@@ -829,25 +839,36 @@ class DesktopTasksController(
/**
* Perform checks required on drag move. Create/release fullscreen indicator as needed.
+ * Different sources for x and y coordinates are used due to different needs for each:
+ * We want split transitions to be based on input coordinates but fullscreen transition
+ * to be based on task edge coordinate.
*
* @param taskInfo the task being dragged.
* @param taskSurface SurfaceControl of dragged task.
- * @param y coordinate of dragged task. Used for checks against status bar height.
+ * @param inputCoordinate coordinates of input. Used for checks against left/right edge of screen.
+ * @param taskBounds bounds of dragged task. Used for checks against status bar height.
*/
fun onDragPositioningMove(
- taskInfo: RunningTaskInfo,
- taskSurface: SurfaceControl,
- y: Float
+ taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl,
+ inputCoordinate: PointF,
+ taskBounds: Rect
) {
- if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
- if (y <= transitionAreaHeight && visualIndicator == null) {
- visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
- displayController, context, taskSurface, shellTaskOrganizer,
- rootTaskDisplayAreaOrganizer)
- visualIndicator?.createFullscreenIndicatorWithAnimatedBounds()
- } else if (y > transitionAreaHeight && visualIndicator != null) {
- releaseVisualIndicator()
- }
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+ if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return
+ var type = DesktopModeVisualIndicator.determineIndicatorType(inputCoordinate,
+ taskBounds, displayLayout, context)
+ if (type != DesktopModeVisualIndicator.INVALID_INDICATOR && visualIndicator == null) {
+ visualIndicator = DesktopModeVisualIndicator(
+ syncQueue, taskInfo,
+ displayController, context, taskSurface, shellTaskOrganizer,
+ rootTaskDisplayAreaOrganizer, type)
+ visualIndicator?.createIndicatorWithAnimatedBounds()
+ return
+ }
+ if (visualIndicator?.eventOutsideRange(inputCoordinate.x,
+ taskBounds.top.toFloat()) == true) {
+ releaseVisualIndicator()
}
}
@@ -856,19 +877,39 @@ class DesktopTasksController(
*
* @param taskInfo the task being dragged.
* @param position position of surface when drag ends.
- * @param y the Y position of the top edge of the task
+ * @param inputCoordinate the coordinates of the motion event
+ * @param taskBounds the updated bounds of the task being dragged.
* @param windowDecor the window decoration for the task being dragged
*/
fun onDragPositioningEnd(
- taskInfo: RunningTaskInfo,
- position: Point,
- y: Float,
- windowDecor: DesktopModeWindowDecoration
+ taskInfo: RunningTaskInfo,
+ position: Point,
+ inputCoordinate: PointF,
+ taskBounds: Rect,
+ windowDecor: DesktopModeWindowDecoration
) {
- if (y <= transitionAreaHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) {
+ return
+ }
+ if (taskBounds.top <= transitionAreaHeight) {
windowDecor.incrementRelayoutBlock()
moveToFullscreenWithAnimation(taskInfo, position)
}
+ if (inputCoordinate.x <= transitionAreaWidth) {
+ releaseVisualIndicator()
+ var wct = WindowContainerTransaction()
+ addMoveToSplitChanges(wct, taskInfo)
+ splitScreenController.requestEnterSplitSelect(taskInfo, wct,
+ SPLIT_POSITION_TOP_OR_LEFT, taskBounds)
+ }
+ if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width()
+ ?.minus(transitionAreaWidth) ?: return)) {
+ releaseVisualIndicator()
+ var wct = WindowContainerTransaction()
+ addMoveToSplitChanges(wct, taskInfo)
+ splitScreenController.requestEnterSplitSelect(taskInfo, wct,
+ SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds)
+ }
}
/**
@@ -892,8 +933,8 @@ class DesktopTasksController(
if (visualIndicator == null) {
visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
displayController, context, taskSurface, shellTaskOrganizer,
- rootTaskDisplayAreaOrganizer)
- visualIndicator?.createFullscreenIndicator()
+ rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR)
+ visualIndicator?.createIndicatorWithAnimatedBounds()
}
val indicator = visualIndicator ?: return
if (y >= getFreeformTransitionStatusBarDragThreshold(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl
index 7171da5d885d..a25f39148b89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl
@@ -17,7 +17,7 @@
package com.android.wm.shell.splitscreen;
import android.app.ActivityManager.RunningTaskInfo;
-
+import android.graphics.Rect;
/**
* Listener interface that Launcher attaches to SystemUI to get split-select callbacks.
*/
@@ -25,5 +25,5 @@ interface ISplitSelectListener {
/**
* Called when a task requests to enter split select
*/
- boolean onRequestSplitSelect(in RunningTaskInfo taskInfo);
+ boolean onRequestSplitSelect(in RunningTaskInfo taskInfo, int splitPosition, in Rect taskBounds);
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index f20fe0b88e12..ad4049320d93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -66,7 +66,8 @@ public interface SplitScreen {
/** Callback interface for listening to requests to enter split select */
interface SplitSelectListener {
- default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo) {
+ default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
+ int splitPosition, Rect taskBounds) {
return false;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 210bf68f3d4f..f90ee586e696 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -507,10 +507,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
* Move a task to split select
* @param taskInfo the task being moved to split select
* @param wct transaction to apply if this is a valid request
+ * @param splitPosition the split position this task should move to
+ * @param taskBounds current freeform bounds of the task entering split
*/
public void requestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
- WindowContainerTransaction wct) {
- mStageCoordinator.requestEnterSplitSelect(taskInfo, wct);
+ WindowContainerTransaction wct, int splitPosition, Rect taskBounds) {
+ mStageCoordinator.requestEnterSplitSelect(taskInfo, wct, splitPosition, taskBounds);
}
public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
@@ -1135,9 +1137,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
new SplitScreen.SplitSelectListener() {
@Override
public boolean onRequestEnterSplitSelect(
- ActivityManager.RunningTaskInfo taskInfo) {
+ ActivityManager.RunningTaskInfo taskInfo, int splitPosition,
+ Rect taskBounds) {
AtomicBoolean result = new AtomicBoolean(false);
- mSelectListener.call(l -> result.set(l.onRequestSplitSelect(taskInfo)));
+ mSelectListener.call(l -> result.set(l.onRequestSplitSelect(taskInfo,
+ splitPosition, taskBounds)));
return result.get();
}
};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 697006868e5a..842b1bf9e8af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -466,10 +466,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
void requestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
- WindowContainerTransaction wct) {
+ WindowContainerTransaction wct, int splitPosition, Rect taskBounds) {
boolean enteredSplitSelect = false;
for (SplitScreen.SplitSelectListener listener : mSelectListeners) {
- enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo);
+ enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo, splitPosition,
+ taskBounds);
}
if (enteredSplitSelect) mTaskOrganizer.applyTransaction(wct);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 026e973f881a..92b44d43567d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -36,6 +36,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
@@ -507,7 +508,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo,
- decoration.mTaskSurface, newTaskBounds.top));
+ decoration.mTaskSurface,
+ new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
+ newTaskBounds));
mIsDragging = true;
mShouldClick = false;
return true;
@@ -536,7 +539,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
- position, newTaskBounds.top, mWindowDecorByTaskId.get(mTaskId)));
+ position,
+ new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
+ newTaskBounds, mWindowDecorByTaskId.get(mTaskId)));
mIsDragging = false;
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
index 5e6697289d5a..7669e79b42be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
@@ -3,7 +3,10 @@ siyuanh@google.com
hughchen@google.com
timhypeng@google.com
robertluo@google.com
-changbetty@google.com
songferngwang@google.com
+yqian@google.com
+chelseahao@google.com
+yiyishen@google.com
+hahong@google.com
# Emergency approvers in case the above are not available
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2077af86a653..c92fe2251f68 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -326,6 +326,17 @@ filegroup {
"tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt",
+
+ /* Bouncer UI tests */
+ "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java",
+ "tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java",
+ "tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java",
+ "tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt",
],
path: "tests/src",
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 7545ff464bab..8a8557aa1f43 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextField
@@ -45,7 +44,6 @@ import androidx.compose.ui.unit.dp
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
/** UI for the input part of a password-requiring version of the bouncer. */
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun PasswordBouncer(
viewModel: PasswordBouncerViewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 64227b8c5f2a..03efbe0fe1ff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -21,6 +21,8 @@ import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -199,33 +201,39 @@ internal fun PatternBouncer(
.onSizeChanged { containerSize = it }
.thenIf(isInputEnabled) {
Modifier.pointerInput(Unit) {
- detectDragGestures(
- onDragStart = { start ->
- inputPosition = start
- viewModel.onDragStart()
- },
- onDragEnd = {
- inputPosition = null
- if (isAnimationEnabled) {
- lineFadeOutAnimatables.values.forEach { animatable ->
- // Launch using the longer-lived scope because we want these
- // animations to proceed to completion even if the surrounding
- // scope is canceled.
- scope.launch { animatable.animateTo(1f) }
+ awaitEachGesture {
+ awaitFirstDown()
+ viewModel.onDown()
+ }
+ }
+ .pointerInput(Unit) {
+ detectDragGestures(
+ onDragStart = { start ->
+ inputPosition = start
+ viewModel.onDragStart()
+ },
+ onDragEnd = {
+ inputPosition = null
+ if (isAnimationEnabled) {
+ lineFadeOutAnimatables.values.forEach { animatable ->
+ // Launch using the longer-lived scope because we want these
+ // animations to proceed to completion even if the
+ // surrounding scope is canceled.
+ scope.launch { animatable.animateTo(1f) }
+ }
}
- }
- viewModel.onDragEnd()
- },
- ) { change, _ ->
- inputPosition = change.position
- viewModel.onDrag(
- xPx = change.position.x,
- yPx = change.position.y,
- containerSizePx = containerSize.width,
- verticalOffsetPx = verticalOffset,
- )
+ viewModel.onDragEnd()
+ },
+ ) { change, _ ->
+ inputPosition = change.position
+ viewModel.onDrag(
+ xPx = change.position.x,
+ yPx = change.position.y,
+ containerSizePx = containerSize.width,
+ verticalOffsetPx = verticalOffset,
+ )
+ }
}
- }
}
) {
if (isAnimationEnabled) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index ec6e5eda264e..e5c69777503c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -24,6 +24,8 @@ import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -76,7 +78,13 @@ internal fun PinBouncer(
Column(
horizontalAlignment = Alignment.CenterHorizontally,
- modifier = modifier,
+ modifier =
+ modifier.pointerInput(Unit) {
+ awaitEachGesture {
+ awaitFirstDown()
+ viewModel.onDown()
+ }
+ }
) {
PinInputDisplay(viewModel)
Spacer(Modifier.height(100.dp))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 0a100babde75..b3b44cba832b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,20 +14,33 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
+@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
package com.android.systemui.keyguard.ui.composable
import android.view.View
import android.view.ViewGroup
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.toComposeRect
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -67,8 +80,8 @@ constructor(
modifier: Modifier,
) {
LockscreenScene(
- viewModel = viewModel,
viewProvider = viewProvider,
+ longPressViewModel = viewModel.longPress,
modifier = modifier,
)
}
@@ -85,23 +98,70 @@ constructor(
@Composable
private fun LockscreenScene(
- viewModel: LockscreenSceneViewModel,
viewProvider: () -> View,
+ longPressViewModel: KeyguardLongPressViewModel,
modifier: Modifier = Modifier,
) {
- AndroidView(
- factory = { _ ->
- val keyguardRootView = viewProvider()
- // Remove the KeyguardRootView from any parent it might already have in legacy code just
- // in case (a view can't have two parents).
- (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
- keyguardRootView
- },
- update = { keyguardRootView ->
- keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener {
- viewModel.onLockButtonClicked()
- }
- },
+ var settingsMenu: View? = null
+
+ Box(
modifier = modifier,
+ ) {
+ LongPressSurface(
+ viewModel = longPressViewModel,
+ isSettingsMenuVisible = { settingsMenu?.isVisible == true },
+ settingsMenuBounds = {
+ val bounds = android.graphics.Rect()
+ settingsMenu?.getHitRect(bounds)
+ bounds.toComposeRect()
+ },
+ modifier = Modifier.fillMaxSize(),
+ )
+
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy code
+ // just in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ settingsMenu = keyguardRootView.requireViewById(R.id.keyguard_settings_button)
+ keyguardRootView
+ },
+ update = { keyguardRootView ->
+ keyguardRootView.requireViewById<View>(R.id.lock_icon_view)
+ },
+ modifier = Modifier.fillMaxSize(),
+ )
+ }
+}
+
+@Composable
+private fun LongPressSurface(
+ viewModel: KeyguardLongPressViewModel,
+ isSettingsMenuVisible: () -> Boolean,
+ settingsMenuBounds: () -> Rect,
+ modifier: Modifier = Modifier,
+) {
+ val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+
+ Box(
+ modifier =
+ modifier
+ .combinedClickable(
+ enabled = isEnabled,
+ onLongClick = viewModel::onLongPress,
+ onClick = {},
+ )
+ .pointerInput(Unit) {
+ awaitEachGesture {
+ val pointerInputChange = awaitFirstDown()
+ if (
+ isSettingsMenuVisible() &&
+ !settingsMenuBounds().contains(pointerInputChange.position)
+ ) {
+ viewModel.onTouchedOutside()
+ }
+ }
+ },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 774c409f34df..40b0b4a3eaa3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -17,11 +17,7 @@
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
@@ -54,17 +50,6 @@ class GoneScene @Inject constructor() : ComposableScene {
override fun SceneScope.Content(
modifier: Modifier,
) {
- /*
- * TODO(b/279501596): once we start testing with the real Content Dynamics Framework,
- * replace this with an error to make sure it doesn't get rendered.
- */
- Box(modifier = modifier) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.align(Alignment.Center)
- ) {
- Text("Gone", style = MaterialTheme.typography.headlineMedium)
- }
- }
+ Box(modifier = modifier)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index ffb20d87a7f8..31cbcb90769a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalComposeUiApi::class)
+
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.fillMaxSize
@@ -25,6 +27,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.motionEventSpy
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.motionEventSpy
+import androidx.compose.ui.input.pointer.pointerInput
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
@@ -82,7 +87,18 @@ fun SceneContainer(
onChangeScene = viewModel::onSceneChanged,
transitions = SceneContainerTransitions,
state = state,
- modifier = modifier.fillMaxSize().motionEventSpy { viewModel.onUserInput() },
+ modifier =
+ modifier
+ .fillMaxSize()
+ .motionEventSpy { event -> viewModel.onMotionEvent(event) }
+ .pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ awaitPointerEvent(PointerEventPass.Final)
+ viewModel.onMotionEventComplete()
+ }
+ }
+ }
) {
sceneByKey.forEach { (sceneKey, composableScene) ->
scene(
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 8611dbbbcb70..1d37809a382e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -142,11 +142,13 @@ public class LockIconView extends FrameLayout implements Dumpable {
mLockIconCenter.y + mRadius);
final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- lp.width = (int) (mSensorRect.right - mSensorRect.left);
- lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
- lp.topMargin = (int) mSensorRect.top;
- lp.setMarginStart((int) mSensorRect.left);
- setLayoutParams(lp);
+ if (lp != null) {
+ lp.width = (int) (mSensorRect.right - mSensorRect.left);
+ lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
+ lp.topMargin = (int) mSensorRect.top;
+ lp.setMarginStart((int) mSensorRect.left);
+ setLayoutParams(lp);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 951a6aeef11b..ab9b647f5c2c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -28,6 +28,7 @@ import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLE
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
@@ -74,7 +75,6 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.io.PrintWriter;
@@ -90,7 +90,7 @@ import javax.inject.Inject;
* icon will show a set distance from the bottom of the device.
*/
@SysUISingleton
-public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
+public class LockIconViewController implements Dumpable {
private static final String TAG = "LockIconViewController";
private static final float sDefaultDensity =
(float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
@@ -109,6 +109,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final DelayableExecutor mExecutor;
private boolean mUdfpsEnrolled;
+ private Resources mResources;
+ private Context mContext;
@NonNull private final AnimatedStateListDrawable mIcon;
@@ -120,6 +122,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
@NonNull private final KeyguardInteractor mKeyguardInteractor;
+ @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
private VelocityTracker mVelocityTracker;
@@ -154,6 +157,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private boolean mDownDetected;
private final Rect mSensorTouchLocation = new Rect();
+ private LockIconView mView;
@VisibleForTesting
final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> {
@@ -178,7 +182,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@Inject
public LockIconViewController(
- @Nullable LockIconView view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull KeyguardViewController keyguardViewController,
@@ -195,9 +198,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull KeyguardTransitionInteractor transitionInteractor,
@NonNull KeyguardInteractor keyguardInteractor,
@NonNull FeatureFlags featureFlags,
- PrimaryBouncerInteractor primaryBouncerInteractor
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ Context context
) {
- super(view);
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mAuthController = authController;
@@ -218,16 +221,40 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
mIcon = (AnimatedStateListDrawable)
- resources.getDrawable(R.drawable.super_lock_icon, mView.getContext().getTheme());
- mView.setImageDrawable(mIcon);
+ resources.getDrawable(R.drawable.super_lock_icon, context.getTheme());
mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
dumpManager.registerDumpable(TAG, this);
+ mResources = resources;
+ mContext = context;
+
+ mAccessibilityDelegate = new View.AccessibilityDelegate() {
+ private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
+ mResources.getString(R.string.accessibility_authenticate_hint));
+ private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
+ mResources.getString(R.string.accessibility_enter_hint));
+ public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(v, info);
+ if (isActionable()) {
+ if (mShowLockIcon) {
+ info.addAction(mAccessibilityAuthenticateHint);
+ } else if (mShowUnlockIcon) {
+ info.addAction(mAccessibilityEnterHint);
+ }
+ }
+ }
+ };
}
- @Override
- protected void onInit() {
+ /** Sets the LockIconView to the controller and rebinds any that depend on it. */
+ public void setLockIconView(LockIconView lockIconView) {
+ mView = lockIconView;
+ mView.setImageDrawable(mIcon);
mView.setAccessibilityDelegate(mAccessibilityDelegate);
if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -240,10 +267,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
mIsActiveDreamLockscreenHostedCallback);
}
- }
- @Override
- protected void onViewAttached() {
updateIsUdfpsEnrolled();
updateConfiguration();
updateKeyguardShowing();
@@ -256,39 +280,57 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mStatusBarState = mStatusBarStateController.getState();
updateColors();
- mConfigurationController.addCallback(mConfigurationListener);
+ mDownDetected = false;
+ updateBurnInOffsets();
+ updateVisibility();
+
+ updateAccessibility();
+
+ lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ registerCallbacks();
+ }
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ unregisterCallbacks();
+ }
+ });
+
+ if (lockIconView.isAttachedToWindow()) {
+ registerCallbacks();
+ }
+ }
+
+ private void registerCallbacks() {
+ mConfigurationController.addCallback(mConfigurationListener);
mAuthController.addCallback(mAuthControllerCallback);
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mKeyguardStateController.addCallback(mKeyguardStateCallback);
- mDownDetected = false;
- updateBurnInOffsets();
- updateVisibility();
-
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
- updateAccessibility();
- }
- private void updateAccessibility() {
- if (mAccessibilityManager.isEnabled()) {
- mView.setOnClickListener(mA11yClickListener);
- } else {
- mView.setOnClickListener(null);
- }
}
- @Override
- protected void onViewDetached() {
+ private void unregisterCallbacks() {
mAuthController.removeCallback(mAuthControllerCallback);
mConfigurationController.removeCallback(mConfigurationListener);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mKeyguardStateController.removeCallback(mKeyguardStateCallback);
-
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
+
+ }
+
+ private void updateAccessibility() {
+ if (mAccessibilityManager.isEnabled()) {
+ mView.setOnClickListener(mA11yClickListener);
+ } else {
+ mView.setOnClickListener(null);
+ }
}
public float getTop() {
@@ -363,28 +405,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
}
}
- private final View.AccessibilityDelegate mAccessibilityDelegate =
- new View.AccessibilityDelegate() {
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- getResources().getString(R.string.accessibility_authenticate_hint));
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- getResources().getString(R.string.accessibility_enter_hint));
- public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(v, info);
- if (isActionable()) {
- if (mShowLockIcon) {
- info.addAction(mAccessibilityAuthenticateHint);
- } else if (mShowUnlockIcon) {
- info.addAction(mAccessibilityEnterHint);
- }
- }
- }
- };
-
private boolean isLockScreen() {
return !mIsDozing
&& !mIsBouncerShowing
@@ -401,18 +421,15 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
}
private void updateConfiguration() {
- WindowManager windowManager = getContext().getSystemService(WindowManager.class);
+ WindowManager windowManager = mContext.getSystemService(WindowManager.class);
Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
mWidthPixels = bounds.right;
mHeightPixels = bounds.bottom;
- mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
- mDefaultPaddingPx =
- getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
-
- mUnlockedLabel = mView.getContext().getResources().getString(
+ mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
+ mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding);
+ mUnlockedLabel = mResources.getString(
R.string.accessibility_unlock_button);
- mLockedLabel = mView.getContext()
- .getResources().getString(R.string.accessibility_lock_icon);
+ mLockedLabel = mResources.getString(R.string.accessibility_lock_icon);
updateLockIconLocation();
}
@@ -755,7 +772,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
} else {
mVibrator.vibrate(
Process.myUid(),
- getContext().getOpPackageName(),
+ mContext.getOpPackageName(),
UdfpsController.EFFECT_CLICK,
"lock-icon-down",
TOUCH_VIBRATION_ATTRIBUTES);
@@ -769,7 +786,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
} else {
mVibrator.vibrate(
Process.myUid(),
- getContext().getOpPackageName(),
+ mContext.getOpPackageName(),
UdfpsController.EFFECT_CLICK,
"lock-screen-lock-icon-longpress",
TOUCH_VIBRATION_ATTRIBUTES);
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 1bf3a9ead08e..fc32f4c138d7 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -22,6 +22,8 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
+import com.android.systemui.classifier.FalsingClassifier
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -50,6 +52,7 @@ constructor(
private val authenticationInteractor: AuthenticationInteractor,
private val sceneInteractor: SceneInteractor,
featureFlags: FeatureFlags,
+ private val falsingInteractor: FalsingInteractor,
) {
/** The user-facing message to show in the bouncer. */
@@ -103,6 +106,34 @@ constructor(
}
}
+ /** Notifies that the user has places down a pointer, not necessarily dragging just yet. */
+ fun onDown() {
+ falsingInteractor.avoidGesture()
+ }
+
+ /**
+ * Notifies of "intentional" (i.e. non-false) user interaction with the UI which is very likely
+ * to be real user interaction with the bouncer and not the result of a false touch in the
+ * user's pocket or by the user's face while holding their device up to their ear.
+ */
+ fun onIntentionalUserInput() {
+ falsingInteractor.updateFalseConfidence(FalsingClassifier.Result.passed(0.6))
+ }
+
+ /**
+ * Notifies of false input which is very likely to be the result of a false touch in the user's
+ * pocket or by the user's face while holding their device up to their ear.
+ */
+ fun onFalseUserInput() {
+ falsingInteractor.updateFalseConfidence(
+ FalsingClassifier.Result.falsed(
+ /* confidence= */ 0.7,
+ /* context= */ javaClass.simpleName,
+ /* reason= */ "empty pattern input",
+ )
+ )
+ }
+
/**
* Either shows the bouncer or unlocks the device, if the bouncer doesn't need to be shown.
*
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index ca15f4e063a9..80a41ce672ec 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -34,6 +34,7 @@ class PasswordBouncerViewModel(
) {
private val _password = MutableStateFlow("")
+
/** The password entered so far. */
val password: StateFlow<String> = _password.asStateFlow()
@@ -48,6 +49,10 @@ class PasswordBouncerViewModel(
interactor.clearMessage()
}
+ if (password.isNotEmpty()) {
+ interactor.onIntentionalUserInput()
+ }
+
_password.value = password
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 4425f9ffcb5e..85eaf0b8db5a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -46,10 +46,12 @@ class PatternBouncerViewModel(
/** The number of columns in the dot grid. */
val columnCount = 3
+
/** The number of rows in the dot grid. */
val rowCount = 3
private val _selectedDots = MutableStateFlow<LinkedHashSet<PatternDotViewModel>>(linkedSetOf())
+
/** The dots that were selected by the user, in the order of selection. */
val selectedDots: StateFlow<List<PatternDotViewModel>> =
_selectedDots
@@ -61,10 +63,12 @@ class PatternBouncerViewModel(
)
private val _currentDot = MutableStateFlow<PatternDotViewModel?>(null)
+
/** The most-recently selected dot that the user selected. */
val currentDot: StateFlow<PatternDotViewModel?> = _currentDot.asStateFlow()
private val _dots = MutableStateFlow(defaultDots())
+
/** All dots on the grid. */
val dots: StateFlow<List<PatternDotViewModel>> = _dots.asStateFlow()
@@ -76,6 +80,11 @@ class PatternBouncerViewModel(
interactor.resetMessage()
}
+ /** Notifies that the user has placed down a pointer, not necessarily dragging just yet. */
+ fun onDown() {
+ interactor.onDown()
+ }
+
/** Notifies that the user has started a drag gesture across the dot grid. */
fun onDragStart() {
interactor.clearMessage()
@@ -124,11 +133,13 @@ class PatternBouncerViewModel(
dot =
PatternDotViewModel(
x =
- if (hitDot.x > dot.x) dot.x + 1
- else if (hitDot.x < dot.x) dot.x - 1 else dot.x,
+ if (hitDot.x > dot.x) {
+ dot.x + 1
+ } else if (hitDot.x < dot.x) dot.x - 1 else dot.x,
y =
- if (hitDot.y > dot.y) dot.y + 1
- else if (hitDot.y < dot.y) dot.y - 1 else dot.y,
+ if (hitDot.y > dot.y) {
+ dot.y + 1
+ } else if (hitDot.y < dot.y) dot.y - 1 else dot.y,
)
}
}
@@ -148,6 +159,12 @@ class PatternBouncerViewModel(
/** Notifies that the user has ended the drag gesture across the dot grid. */
fun onDragEnd() {
val pattern = _selectedDots.value.map { it.toCoordinate() }
+
+ if (pattern.size == 1) {
+ // Single dot patterns are treated as erroneous/false taps:
+ interactor.onFalseUserInput()
+ }
+
_dots.value = defaultDots()
_currentDot.value = null
_selectedDots.value = linkedSetOf()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 844cf024ef71..ebf939b264fa 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -88,6 +88,11 @@ class PinBouncerViewModel(
interactor.resetMessage()
}
+ /** Notifies that the user has placed down a pointer. */
+ fun onDown() {
+ interactor.onDown()
+ }
+
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
val pinInput = mutablePinInput.value
@@ -95,6 +100,8 @@ class PinBouncerViewModel(
interactor.clearMessage()
}
+ interactor.onIntentionalUserInput()
+
mutablePinInput.value = pinInput.append(input)
tryAuthenticate(useAutoConfirm = true)
}
@@ -148,8 +155,10 @@ class PinBouncerViewModel(
enum class ActionButtonAppearance {
/** Button must not be shown. */
Hidden,
+
/** Button is shown, but with no background to make it less prominent. */
Subtle,
+
/** Button is shown. */
Shown,
}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index 4227a7a67330..b2680950d9c5 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -29,12 +29,14 @@ import android.util.Log
import android.view.WindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -43,10 +45,12 @@ import javax.inject.Inject
* Helps with handling camera-related gestures (for example, double-tap the power button to launch
* the camera).
*/
+@SysUISingleton
class CameraGestureHelper @Inject constructor(
private val context: Context,
private val centralSurfaces: CentralSurfaces,
private val keyguardStateController: KeyguardStateController,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val packageManager: PackageManager,
private val activityManager: ActivityManager,
private val activityStarter: ActivityStarter,
@@ -133,7 +137,7 @@ class CameraGestureHelper @Inject constructor(
centralSurfaces.startLaunchTransitionTimeout()
// Call this to make sure the keyguard is ready to be dismissed once the next intent is
// handled by the OS (in our case it is the activity we started right above)
- centralSurfaces.readyForKeyguardDone()
+ statusBarKeyguardViewManager.readyForKeyguardDone()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index 08e1e9a9a035..f77f98956cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -29,78 +29,21 @@ public interface FalsingCollector {
void setShowingAod(boolean showingAod);
/** */
- void onNotificationStartDraggingDown();
-
- /** */
- void onNotificationStopDraggingDown();
-
- /** */
- void setNotificationExpanded();
-
- /** */
- void onQsDown();
-
- /** */
boolean shouldEnforceBouncer();
/** */
- void onTrackingStarted(boolean secure);
-
- /** */
- void onTrackingStopped();
-
- /** */
- void onLeftAffordanceOn();
-
- /** */
- void onCameraOn();
-
- /** */
- void onAffordanceSwipingStarted(boolean rightCorner);
-
- /** */
- void onAffordanceSwipingAborted();
-
- /** */
- void onStartExpandingFromPulse();
-
- /** */
- void onExpansionFromPulseStopped();
-
- /** */
void onScreenOnFromTouch();
/** */
boolean isReportingEnabled();
/** */
- void onUnlockHintStarted();
-
- /** */
- void onCameraHintStarted();
-
- /** */
- void onLeftAffordanceHintStarted();
-
- /** */
void onScreenTurningOn();
/** */
void onScreenOff();
/** */
- void onNotificationStopDismissing();
-
- /** */
- void onNotificationDismissed();
-
- /** */
- void onNotificationStartDismissing();
-
- /** */
- void onNotificationDoubleTap(boolean accepted, float dx, float dy);
-
- /** */
void onBouncerShown();
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorActual.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorActual.kt
new file mode 100644
index 000000000000..3eaad1fed7bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorActual.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.classifier
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class FalsingCollectorActual
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index f335d1d0475f..c0ee71cf0dc8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -29,59 +29,11 @@ public class FalsingCollectorFake implements FalsingCollector {
}
@Override
- public void onNotificationStartDraggingDown() {
- }
-
- @Override
- public void onNotificationStopDraggingDown() {
- }
-
- @Override
- public void setNotificationExpanded() {
- }
-
- @Override
- public void onQsDown() {
- }
-
- @Override
public boolean shouldEnforceBouncer() {
return false;
}
@Override
- public void onTrackingStarted(boolean secure) {
- }
-
- @Override
- public void onTrackingStopped() {
- }
-
- @Override
- public void onLeftAffordanceOn() {
- }
-
- @Override
- public void onCameraOn() {
- }
-
- @Override
- public void onAffordanceSwipingStarted(boolean rightCorner) {
- }
-
- @Override
- public void onAffordanceSwipingAborted() {
- }
-
- @Override
- public void onStartExpandingFromPulse() {
- }
-
- @Override
- public void onExpansionFromPulseStopped() {
- }
-
- @Override
public void onScreenOnFromTouch() {
}
@@ -91,18 +43,6 @@ public class FalsingCollectorFake implements FalsingCollector {
}
@Override
- public void onUnlockHintStarted() {
- }
-
- @Override
- public void onCameraHintStarted() {
- }
-
- @Override
- public void onLeftAffordanceHintStarted() {
- }
-
- @Override
public void onScreenTurningOn() {
}
@@ -111,22 +51,6 @@ public class FalsingCollectorFake implements FalsingCollector {
}
@Override
- public void onNotificationStopDismissing() {
- }
-
- @Override
- public void onNotificationDismissed() {
- }
-
- @Override
- public void onNotificationStartDismissing() {
- }
-
- @Override
- public void onNotificationDoubleTap(boolean accepted, float dx, float dy) {
- }
-
- @Override
public void onBouncerShown() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 6a021f6a82b6..39c01f759654 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -103,10 +103,6 @@ class FalsingCollectorImpl implements FalsingCollector {
private final BatteryStateChangeCallback mBatteryListener = new BatteryStateChangeCallback() {
@Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- }
-
- @Override
public void onWirelessChargingChanged(boolean isWirelessCharging) {
if (isWirelessCharging || mDockManager.isDocked()) {
mProximitySensor.pause();
@@ -169,34 +165,21 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void onSuccessfulUnlock() {
+ logDebug("REAL: onSuccessfulUnlock");
mFalsingManager.onSuccessfulUnlock();
sessionEnd();
}
@Override
public void setShowingAod(boolean showingAod) {
+ logDebug("REAL: setShowingAod(" + showingAod + ")");
mShowingAod = showingAod;
updateSessionActive();
}
- @Override
- public void onNotificationStartDraggingDown() {
- }
-
- @Override
- public void onNotificationStopDraggingDown() {
- }
-
- @Override
- public void setNotificationExpanded() {
- }
-
- @Override
- public void onQsDown() {
- }
-
@VisibleForTesting
void onQsExpansionChanged(Boolean expanded) {
+ logDebug("REAL: onQsExpansionChanged(" + expanded + ")");
if (expanded) {
unregisterSensors();
} else if (mSessionStarted) {
@@ -210,39 +193,8 @@ class FalsingCollectorImpl implements FalsingCollector {
}
@Override
- public void onTrackingStarted(boolean secure) {
- }
-
- @Override
- public void onTrackingStopped() {
- }
-
- @Override
- public void onLeftAffordanceOn() {
- }
-
- @Override
- public void onCameraOn() {
- }
-
- @Override
- public void onAffordanceSwipingStarted(boolean rightCorner) {
- }
-
- @Override
- public void onAffordanceSwipingAborted() {
- }
-
- @Override
- public void onStartExpandingFromPulse() {
- }
-
- @Override
- public void onExpansionFromPulseStopped() {
- }
-
- @Override
public void onScreenOnFromTouch() {
+ logDebug("REAL: onScreenOnFromTouch");
onScreenTurningOn();
}
@@ -252,52 +204,28 @@ class FalsingCollectorImpl implements FalsingCollector {
}
@Override
- public void onUnlockHintStarted() {
- }
-
- @Override
- public void onCameraHintStarted() {
- }
-
- @Override
- public void onLeftAffordanceHintStarted() {
- }
-
- @Override
public void onScreenTurningOn() {
+ logDebug("REAL: onScreenTurningOn");
mScreenOn = true;
updateSessionActive();
}
@Override
public void onScreenOff() {
+ logDebug("REAL: onScreenOff");
mScreenOn = false;
updateSessionActive();
}
@Override
- public void onNotificationStopDismissing() {
- }
-
- @Override
- public void onNotificationDismissed() {
- }
-
- @Override
- public void onNotificationStartDismissing() {
- }
-
- @Override
- public void onNotificationDoubleTap(boolean accepted, float dx, float dy) {
- }
-
- @Override
public void onBouncerShown() {
+ logDebug("REAL: onBouncerShown");
unregisterSensors();
}
@Override
public void onBouncerHidden() {
+ logDebug("REAL: onBouncerHidden");
if (mSessionStarted) {
registerSensors();
}
@@ -305,6 +233,7 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void onTouchEvent(MotionEvent ev) {
+ logDebug("REAL: onTouchEvent(" + ev.getActionMasked() + ")");
if (!mKeyguardStateController.isShowing()) {
avoidGesture();
return;
@@ -334,6 +263,7 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void onMotionEventComplete() {
+ logDebug("REAL: onMotionEventComplete");
// We must delay processing the completion because of the way Android handles click events.
// It generally delays executing them immediately, instead choosing to give the UI a chance
// to respond to touch events before acknowledging the click. As such, we must also delay,
@@ -350,6 +280,7 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void avoidGesture() {
+ logDebug("REAL: avoidGesture");
mAvoidGesture = true;
if (mPendingDownEvent != null) {
mPendingDownEvent.recycle();
@@ -359,6 +290,7 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void cleanup() {
+ logDebug("REAL: cleanup");
unregisterSensors();
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
@@ -368,11 +300,13 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void updateFalseConfidence(FalsingClassifier.Result result) {
+ logDebug("REAL: updateFalseConfidence(" + result.isFalse() + ")");
mHistoryTracker.addResults(Collections.singleton(result), mSystemClock.uptimeMillis());
}
@Override
public void onA11yAction() {
+ logDebug("REAL: onA11yAction");
if (mPendingDownEvent != null) {
mPendingDownEvent.recycle();
mPendingDownEvent = null;
@@ -427,17 +361,13 @@ class FalsingCollectorImpl implements FalsingCollector {
static void logDebug(String msg) {
- logDebug(msg, null);
- }
-
- static void logDebug(String msg, Throwable throwable) {
if (DEBUG) {
- Log.d(TAG, msg, throwable);
+ logDebug(msg);
}
}
private static class ProximityEventImpl implements FalsingManager.ProximityEvent {
- private ThresholdSensorEvent mThresholdSensorEvent;
+ private final ThresholdSensorEvent mThresholdSensorEvent;
ProximityEventImpl(ThresholdSensorEvent thresholdSensorEvent) {
mThresholdSensorEvent = thresholdSensorEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
new file mode 100644
index 000000000000..e5b404f30889
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.classifier
+
+import android.view.MotionEvent
+import com.android.systemui.classifier.FalsingCollectorImpl.logDebug
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class FalsingCollectorNoOp @Inject constructor() : FalsingCollector {
+ override fun onSuccessfulUnlock() {
+ logDebug("NOOP: onSuccessfulUnlock")
+ }
+
+ override fun setShowingAod(showingAod: Boolean) {
+ logDebug("NOOP: setShowingAod($showingAod)")
+ }
+
+ override fun shouldEnforceBouncer(): Boolean = false
+
+ override fun onScreenOnFromTouch() {
+ logDebug("NOOP: onScreenOnFromTouch")
+ }
+
+ override fun isReportingEnabled(): Boolean = false
+
+ override fun onScreenTurningOn() {
+ logDebug("NOOP: onScreenTurningOn")
+ }
+
+ override fun onScreenOff() {
+ logDebug("NOOP: onScreenOff")
+ }
+
+ override fun onBouncerShown() {
+ logDebug("NOOP: onBouncerShown")
+ }
+
+ override fun onBouncerHidden() {
+ logDebug("NOOP: onBouncerHidden")
+ }
+
+ override fun onTouchEvent(ev: MotionEvent) {
+ logDebug("NOOP: onTouchEvent(${ev.actionMasked})")
+ }
+
+ override fun onMotionEventComplete() {
+ logDebug("NOOP: onMotionEventComplete")
+ }
+
+ override fun avoidGesture() {
+ logDebug("NOOP: avoidGesture")
+ }
+
+ override fun cleanup() {
+ logDebug("NOOP: cleanup")
+ }
+
+ override fun updateFalseConfidence(result: FalsingClassifier.Result) {
+ logDebug("NOOP: updateFalseConfidence(${result.isFalse})")
+ }
+
+ override fun onA11yAction() {
+ logDebug("NOOP: onA11yAction")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index c7f3b2d08efd..3195d093a711 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -22,19 +22,21 @@ import android.view.ViewConfiguration;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.phone.NotificationTapHelper;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Named;
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.ElementsIntoSet;
-
/** Dagger Module for Falsing. */
@Module
public interface FalsingModule {
@@ -45,10 +47,20 @@ public interface FalsingModule {
String DOUBLE_TAP_TIMEOUT_MS = "falsing_double_tap_timeout_ms";
String IS_FOLDABLE_DEVICE = "falsing_foldable_device";
- /** */
- @Binds
+ /** Provides the actual {@link FalsingCollector} if the scene container feature is off. */
+ @Provides
@SysUISingleton
- FalsingCollector bindsFalsingCollector(FalsingCollectorImpl impl);
+ static FalsingCollector providesFalsingCollectorLegacy(
+ FalsingCollectorImpl impl,
+ FalsingCollectorNoOp noOp,
+ FeatureFlagsClassic featureFlags) {
+ return featureFlags.isEnabled(Flags.SCENE_CONTAINER) ? noOp : impl;
+ }
+
+ /** Provides the actual {@link FalsingCollector}. */
+ @Binds
+ @FalsingCollectorActual
+ FalsingCollector bindsFalsingCollectorActual(FalsingCollectorImpl impl);
/** */
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
new file mode 100644
index 000000000000..2e861c399ee9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.classifier.domain.interactor
+
+import android.view.MotionEvent
+import com.android.systemui.classifier.FalsingClassifier
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorActual
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Exposes the subset of the [FalsingCollector] API that's required by external callers.
+ *
+ * E.g. methods of [FalsingCollector] that are not exposed by this class don't need to be invoked by
+ * external callers as they're already called by the scene framework.
+ */
+@SysUISingleton
+class FalsingInteractor
+@Inject
+constructor(
+ @FalsingCollectorActual private val collector: FalsingCollector,
+) {
+ /**
+ * Notifies of a [MotionEvent] that passed through the UI.
+ *
+ * Must call [onMotionEventComplete] when done with this event.
+ */
+ fun onTouchEvent(event: MotionEvent) = collector.onTouchEvent(event)
+
+ /**
+ * Notifies that a [MotionEvent] has finished being dispatched through the UI.
+ *
+ * Must be called after each call to [onTouchEvent].
+ */
+ fun onMotionEventComplete() = collector.onMotionEventComplete()
+
+ /**
+ * Instructs the falsing system to ignore the rest of the current input gesture; automatically
+ * resets when another gesture is started (with the next down event).
+ */
+ fun avoidGesture() = collector.avoidGesture()
+
+ /**
+ * Inserts the given [result] into the falsing system, affecting future runs of the classifier
+ * as if this was a result that had organically happened before.
+ */
+ fun updateFalseConfidence(
+ result: FalsingClassifier.Result,
+ ) = collector.updateFalseConfidence(result)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index c3369da68821..970b475fe702 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -16,10 +16,10 @@
package com.android.systemui.communal.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import javax.inject.Inject
/** Blueprint for communal mode. */
@@ -28,13 +28,10 @@ import javax.inject.Inject
class DefaultCommunalBlueprint
@Inject
constructor(
- private val defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
+ defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
-
- override fun apply(constraintSet: ConstraintSet) {
- defaultCommunalWidgetSection.apply(constraintSet)
- }
+ override val sections: Array<KeyguardSection> = arrayOf(defaultCommunalWidgetSection)
companion object {
const val COMMUNAL = "communal"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
index b0e3132a1fc7..4fb9384630a5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
@@ -17,18 +17,47 @@
package com.android.systemui.communal.ui.view.layout.sections
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
+import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
+import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import dagger.Lazy
import javax.inject.Inject
-class DefaultCommunalWidgetSection @Inject constructor() : KeyguardSection {
+class DefaultCommunalWidgetSection
+@Inject
+constructor(
+ private val featureFlags: FeatureFlags,
+ private val keyguardRootView: KeyguardRootView,
+ private val communalWidgetViewModel: CommunalWidgetViewModel,
+ private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
+ private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
+) : KeyguardSection {
private val widgetAreaViewId = R.id.communal_widget_wrapper
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
+ return
+ }
+
+ CommunalWidgetViewBinder.bind(
+ keyguardRootView,
+ communalWidgetViewModel,
+ communalWidgetViewAdapter,
+ keyguardBlueprintInteractor.get(),
+ )
+ }
- override fun apply(constraintSet: ConstraintSet) {
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(widgetAreaViewId, WRAP_CONTENT)
constrainHeight(widgetAreaViewId, WRAP_CONTENT)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index d3174f11ace5..adb0bf38b51f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -71,14 +71,9 @@ open class ControlsServiceInfo(
private var resolved: Boolean = false
@WorkerThread
- fun resolvePanelActivity(
- allowAllApps: Boolean = false
- ) {
+ fun resolvePanelActivity() {
if (resolved) return
resolved = true
- val validPackages = context.resources
- .getStringArray(R.array.config_controlsPreferredPackages)
- if (componentName.packageName !in validPackages && !allowAllApps) return
panelActivity = _panelActivity?.let {
val resolveInfos = mPm.queryIntentActivitiesAsUser(
Intent().setComponent(it),
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 83bec664876d..74e1dc0b0fad 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -33,7 +33,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.ActivityTaskManagerProxy
import com.android.systemui.util.asIndenting
@@ -125,9 +124,8 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
private fun updateServices(newServices: List<ControlsServiceInfo>) {
if (activityTaskManagerProxy.supportsMultiWindow(context)) {
- val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
newServices.forEach {
- it.resolvePanelActivity(allowAllApps) }
+ it.resolvePanelActivity() }
}
if (newServices != availableServices) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 6b578baddcd2..907e106d1151 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -39,6 +39,10 @@ object Flags {
@JvmField val TEAMFOOD = unreleasedFlag("teamfood")
// 100 - notification
+ // TODO(b/297792660): Tracking Bug
+ val ADD_TRANSIENT_HUN_IN_STACK_STATE_ANIMATOR =
+ unreleasedFlag("add_transient_hun_in_stack_state_animator", teamfood = false)
+
// TODO(b/254512751): Tracking Bug
val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
unreleasedFlag("notification_pipeline_developer_logging")
@@ -204,6 +208,10 @@ object Flags {
@JvmField
val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag("lock_screen_long_press_enabled")
+ /** Inflate and bind views upon emitting a blueprint value . */
+ // TODO(b/297365780): Tracking Bug
+ @JvmField val LAZY_INFLATE_KEYGUARD = unreleasedFlag("lazy_inflate_keyguard")
+
/** Enables UI updates for AI wallpapers in the wallpaper picker. */
// TODO(b/267722622): Tracking Bug
@JvmField val WALLPAPER_PICKER_UI_FOR_AIWP = releasedFlag("wallpaper_picker_ui_for_aiwp")
@@ -394,7 +402,7 @@ object Flags {
// TODO(b/290676905): Tracking Bug
val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
- unreleasedFlag("new_shade_carrier_group_mobile_icons")
+ unreleasedFlag("new_shade_carrier_group_mobile_icons", teamfood = true)
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
@@ -634,9 +642,6 @@ object Flags {
// 1900
@JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
- // 2000 - device controls
- @JvmField val APP_PANELS_ALL_APPS_ALLOWED = releasedFlag("app_panels_all_apps_allowed")
-
// 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.)
// TODO(b/259264861): Tracking Bug
@JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag("udfps_new_touch_detection")
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
index c41b5e4d319b..285601116d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -366,6 +366,52 @@ constructor(
}
}
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param icon an [Icon] representing the source of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ suspend fun loadSize(icon: Icon, context: Context): Size? =
+ withContext(backgroundDispatcher) { loadSizeSync(icon, context) }
+
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param icon an [Icon] representing the source of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ @WorkerThread
+ fun loadSizeSync(icon: Icon, context: Context): Size? {
+ return when (icon.type) {
+ Icon.TYPE_URI,
+ Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+ val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
+ loadSizeSync(source)
+ }
+ else -> null
+ }
+ }
+
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param source [ImageDecoder.Source] of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ @WorkerThread
+ fun loadSizeSync(source: ImageDecoder.Source): Size? {
+ return try {
+ ImageDecoder.decodeHeader(source).size
+ } catch (e: IOException) {
+ Log.w(TAG, "Failed to load source $source", e)
+ return null
+ } catch (e: DecodeException) {
+ Log.w(TAG, "Failed to decode source $source", e)
+ return null
+ }
+ }
+
companion object {
const val TAG = "ImageLoader"
@@ -452,7 +498,7 @@ constructor(
* originate from other processes so we need to make sure we load them from the right
* package source.
*
- * @return [Resources] to load the icon drawble or null if icon doesn't carry a resource or
+ * @return [Resources] to load the icon drawable or null if icon doesn't carry a resource or
* the resource package couldn't be resolved.
*/
@WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
new file mode 100644
index 000000000000..629b361064a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.haptics.slider
+
+import android.widget.SeekBar
+import android.widget.SeekBar.OnSeekBarChangeListener
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+/** An event producer for a Seekable element such as the Android [SeekBar] */
+class SeekableSliderEventProducer : SliderEventProducer, OnSeekBarChangeListener {
+
+ /** The current event reported by a SeekBar */
+ private val _currentEvent = MutableStateFlow(SliderEvent(SliderEventType.NOTHING, 0f))
+
+ override fun produceEvents(): Flow<SliderEvent> = _currentEvent.asStateFlow()
+
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ val eventType =
+ if (fromUser) SliderEventType.PROGRESS_CHANGE_BY_USER
+ else SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
+
+ _currentEvent.value = SliderEvent(eventType, normalizeProgress(seekBar, progress))
+ }
+
+ /**
+ * Normalize the integer progress of a SeekBar to the range from 0F to 1F.
+ *
+ * @param[seekBar] The SeekBar that reports a progress.
+ * @param[progress] The integer progress of the SeekBar within its min and max values.
+ * @return The progress in the range from 0F to 1F.
+ */
+ private fun normalizeProgress(seekBar: SeekBar, progress: Int): Float {
+ if (seekBar.max == seekBar.min) {
+ return 1.0f
+ }
+ val range = seekBar.max - seekBar.min
+ return (progress - seekBar.min) / range.toFloat()
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
+ _currentEvent.update { previousEvent ->
+ SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, previousEvent.currentProgress)
+ }
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
+ _currentEvent.update { previousEvent ->
+ SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, previousEvent.currentProgress)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEvent.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEvent.kt
new file mode 100644
index 000000000000..1377b29d8d21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEvent.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider
+
+import androidx.annotation.FloatRange
+
+/**
+ * An event arising from a slider.
+ *
+ * @property[type] The type of event. Must be one of [SliderEventType].
+ * @property[currentProgress] The current progress of the slider normalized to the range between 0F
+ * and 1F (inclusive).
+ */
+data class SliderEvent(
+ val type: SliderEventType,
+ @FloatRange(from = 0.0, to = 1.0) val currentProgress: Float
+)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventProducer.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventProducer.kt
new file mode 100644
index 000000000000..8b17e86e7cc8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventProducer.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.haptics.slider
+
+import kotlinx.coroutines.flow.Flow
+
+/** Defines a producer of [SliderEvent] to be consumed as a [Flow] */
+interface SliderEventProducer {
+
+ /**
+ * Produce a stream of [SliderEvent]
+ *
+ * @return A [Flow] of [SliderEvent] produced from the operation of a slider.
+ */
+ fun produceEvents(): Flow<SliderEvent>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
new file mode 100644
index 000000000000..413e27763ba8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider
+
+/** The type of a [SliderEvent]. */
+enum class SliderEventType {
+ /* No event. */
+ NOTHING,
+ /* The slider has captured a touch input and is tracking touch events. */
+ STARTED_TRACKING_TOUCH,
+ /* The slider progress is changing due to user touch input. */
+ PROGRESS_CHANGE_BY_USER,
+ /* The slider progress is changing programmatically. */
+ PROGRESS_CHANGE_BY_PROGRAM,
+ /* The slider has stopped tracking touch events. */
+ STOPPED_TRACKING_TOUCH,
+ /* The external (not touch) stimulus that was modifying the slider progress has stopped. */
+ EXTERNAL_STIMULUS_RELEASE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 9d2771ec4d89..f6add9c66f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -298,18 +298,18 @@ public class KeyguardService extends Service {
}
@Inject
- public KeyguardService(KeyguardViewMediator keyguardViewMediator,
- KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
- ScreenOnCoordinator screenOnCoordinator,
- ShellTransitions shellTransitions,
- DisplayTracker displayTracker,
- WindowManagerLockscreenVisibilityViewModel
- wmLockscreenVisibilityViewModel,
- WindowManagerLockscreenVisibilityManager wmLockscreenVisibilityManager,
- KeyguardSurfaceBehindViewModel keyguardSurfaceBehindViewModel,
- KeyguardSurfaceBehindParamsApplier keyguardSurfaceBehindAnimator,
- @Application CoroutineScope scope,
- FeatureFlags featureFlags) {
+ public KeyguardService(
+ KeyguardViewMediator keyguardViewMediator,
+ KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
+ ScreenOnCoordinator screenOnCoordinator,
+ ShellTransitions shellTransitions,
+ DisplayTracker displayTracker,
+ WindowManagerLockscreenVisibilityViewModel wmLockscreenVisibilityViewModel,
+ WindowManagerLockscreenVisibilityManager wmLockscreenVisibilityManager,
+ KeyguardSurfaceBehindViewModel keyguardSurfaceBehindViewModel,
+ KeyguardSurfaceBehindParamsApplier keyguardSurfaceBehindAnimator,
+ @Application CoroutineScope scope,
+ FeatureFlags featureFlags) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 1cd87955fd84..6bc9abf13cf7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -17,10 +17,15 @@
package com.android.systemui.keyguard
+import android.content.Context
import android.content.res.Configuration
+import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.KeyguardStatusViewController
+import com.android.keyguard.LockIconView
+import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.R
@@ -37,6 +42,7 @@ import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+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
import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
@@ -92,6 +98,9 @@ constructor(
private val communalWidgetViewModel: CommunalWidgetViewModel,
private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
private val notificationStackScrollerLayoutController: NotificationStackScrollLayoutController,
+ private val context: Context,
+ private val keyguardIndicationController: KeyguardIndicationController,
+ private val lockIconViewController: LockIconViewController,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -100,22 +109,41 @@ constructor(
private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
private var settingsPopupMenuHandle: DisposableHandle? = null
- private var keyguardStatusViewController: KeyguardStatusViewController? = null
+ var keyguardStatusViewController: KeyguardStatusViewController? = null
+ get() {
+ if (field == null) {
+ val statusViewComponent =
+ keyguardStatusViewComponentFactory.build(
+ LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, null)
+ as KeyguardStatusView
+ )
+ val controller = statusViewComponent.keyguardStatusViewController
+ controller.init()
+ field = controller
+ }
+
+ return field
+ }
override fun start() {
- bindKeyguardRootView()
- val notificationPanel =
- notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
- unbindKeyguardBottomArea(notificationPanel)
- bindIndicationArea()
- bindLockIconView(notificationPanel)
- bindKeyguardStatusView(notificationPanel)
- setupNotificationStackScrollLayout(notificationPanel)
- bindLeftShortcut()
- bindRightShortcut()
- bindAmbientIndicationArea()
- bindSettingsPopupMenu()
- bindCommunalWidgetArea()
+ if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
+ keyguardRootView.removeAllViews()
+ initializeViews()
+ } else {
+ bindKeyguardRootView()
+ val notificationPanel =
+ notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
+ unbindKeyguardBottomArea(notificationPanel)
+ bindIndicationArea()
+ bindLockIconView(notificationPanel)
+ bindKeyguardStatusView(notificationPanel)
+ setupNotificationStackScrollLayout(notificationPanel)
+ bindLeftShortcut()
+ bindRightShortcut()
+ bindAmbientIndicationArea()
+ bindSettingsPopupMenu()
+ bindCommunalWidgetArea()
+ }
KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
keyguardBlueprintCommandListener.start()
@@ -164,6 +192,14 @@ constructor(
)
}
+ /** Initialize views so that corresponding controllers have a view set. */
+ private fun initializeViews() {
+ val indicationArea = KeyguardIndicationArea(context, null)
+ keyguardIndicationController.setIndicationArea(indicationArea)
+
+ lockIconViewController.setLockIconView(LockIconView(context, null))
+ }
+
private fun bindKeyguardRootView() {
rootViewHandle?.dispose()
rootViewHandle =
@@ -186,6 +222,9 @@ constructor(
keyguardRootView.findViewById<View?>(R.id.lock_icon_view)?.let {
keyguardRootView.removeView(it)
}
+ legacyParent.requireViewById<LockIconView>(R.id.lock_icon_view).let {
+ lockIconViewController.setLockIconView(it)
+ }
}
}
@@ -307,6 +346,5 @@ constructor(
* Temporary, to allow NotificationPanelViewController to use the same instance while code is
* migrated: b/288242803
*/
- fun getKeyguardStatusViewController() = keyguardStatusViewController
fun getKeyguardRootView() = keyguardRootView
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e983a0550c1f..2b4dc8108418 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -3207,7 +3207,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
* visible
*/
public void exitKeyguardAndFinishSurfaceBehindRemoteAnimation(boolean showKeyguard) {
- Log.d(TAG, "onKeyguardExitRemoteAnimationFinished");
+ Log.d(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation");
if (!mSurfaceBehindRemoteAnimationRunning && !mSurfaceBehindRemoteAnimationRequested) {
Log.d(TAG, "skip onKeyguardExitRemoteAnimationFinished showKeyguard=" + showKeyguard
+ " surfaceAnimationRunning=" + mSurfaceBehindRemoteAnimationRunning
@@ -3222,6 +3222,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
// Post layout changes to the next frame, so we don't hang at the end of the animation.
DejankUtils.postAfterTraversal(() -> {
+ if (!mPM.isInteractive()) {
+ Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal" +
+ "Not interactive after traversal. Don't hide the keyguard. This means we " +
+ "re-locked the device during unlock.");
+ return;
+ }
+
onKeyguardExitFinished();
if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index 7234757081e2..f91ae743d956 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -17,13 +17,10 @@
package com.android.systemui.keyguard.data.repository
-import android.view.View
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.core.view.children
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
import java.io.PrintWriter
@@ -95,26 +92,3 @@ constructor(
blueprintIdMap.forEach { entry -> pw.println("${entry.key}") }
}
}
-
-/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
-interface KeyguardBlueprint {
- val id: String
- val shouldRemoveUnconstrainedViews: Boolean
- get() = true
-
- fun apply(constraintLayout: ConstraintSet)
- fun removeUnConstrainedViews(constraintLayout: ConstraintLayout, constraintSet: ConstraintSet) {
- constraintLayout.children
- .map { it.id }
- .filterNot { constraintSet.knownIds.contains(it) }
- .forEach { constraintSet.setVisibility(it, View.GONE) }
- }
-}
-
-/**
- * Lower level modules that determine constraints for a particular section in the lockscreen root
- * view.
- */
-interface KeyguardSection {
- fun apply(constraintSet: ConstraintSet)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index e13f6757112e..0c05a0e33871 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -78,28 +78,37 @@ constructor(
/** Position information for the shared notification container. */
val sharedNotificationContainerPosition =
MutableStateFlow(SharedNotificationContainerPosition())
+
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
*/
val dozeAmount: Flow<Float> = repository.linearDozeAmount
+
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+
/** Receive an event for doze time tick */
val dozeTimeTick: Flow<Long> = repository.dozeTimeTick
+
/** Whether Always-on Display mode is available. */
val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
+
/** Doze transition information. */
val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
+
/**
* Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
* but not vice-versa.
*/
val isDreaming: Flow<Boolean> = repository.isDreaming
+
/** Whether the system is dreaming with an overlay active */
val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
+
/** Whether the system is dreaming and the active dream is hosted in lockscreen */
val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
+
/** Event for when the camera gesture is detected */
val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow {
val callback =
@@ -148,18 +157,25 @@ constructor(
/** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+
/** Whether the keyguard is unlocked or not. */
val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
+
/** Whether the keyguard is occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
+
/** Whether the keyguard is going away. */
val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
+
/** Whether the primary bouncer is showing or not. */
val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow
+
/** Whether the alternate bouncer is showing or not. */
val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
+
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
+
/**
* Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear,
* side, under display) is used to unlock the device.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
new file mode 100644
index 000000000000..659c5f3007e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+
+/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
+interface KeyguardBlueprint {
+ val id: String
+ val sections: Array<KeyguardSection>
+
+ fun addViews(constraintLayout: ConstraintLayout) {
+ sections.forEach { it.addViews(constraintLayout) }
+ }
+
+ fun applyConstraints(constraintSet: ConstraintSet) {
+ sections.forEach { it.applyConstraints(constraintSet) }
+ }
+
+ fun onDestroy() {
+ sections.forEach { it.onDestroy() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
new file mode 100644
index 000000000000..19f50dec3032
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+
+/**
+ * Lower level modules that determine constraints for a particular section in the lockscreen root
+ * view.
+ */
+interface KeyguardSection {
+ fun addViews(constraintLayout: ConstraintLayout)
+ fun applyConstraints(constraintSet: ConstraintSet)
+ fun onDestroy() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
index fb685dab1797..c8a04fdbca52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
@@ -19,18 +19,20 @@ package com.android.systemui.keyguard.shared.model
import android.os.PowerManager
/** The reason we're waking up or going to sleep, such as pressing the power button. */
-enum class WakeSleepReason {
+enum class WakeSleepReason(
+ val isTouch: Boolean,
+) {
/** The physical power button was pressed to wake up or sleep the device. */
- POWER_BUTTON,
+ POWER_BUTTON(isTouch = false),
/** The user has tapped or double tapped to wake the screen. */
- TAP,
+ TAP(isTouch = true),
/** The user performed some sort of gesture to wake the screen. */
- GESTURE,
+ GESTURE(isTouch = true),
/** Something else happened to wake up or sleep the device. */
- OTHER;
+ OTHER(isTouch = false);
companion object {
fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index e40c2793176e..c340e5df498b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -21,6 +21,7 @@ import android.os.Trace
import android.util.Log
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.view.children
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
@@ -36,16 +37,28 @@ class KeyguardBlueprintViewBinder {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.blueprint.collect { blueprint ->
- Trace.beginSection("KeyguardBlueprintController#applyBlueprint")
+ Trace.beginSection("KeyguardBlueprint#applyBlueprint")
Log.d(TAG, "applying blueprint: $blueprint")
- ConstraintSet().apply {
- clone(constraintLayout)
- val emptyLayout = ConstraintSet.Layout()
- knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) }
- blueprint?.apply(this)
- blueprint?.removeUnConstrainedViews(constraintLayout, this)
- applyTo(constraintLayout)
+ if (blueprint != viewModel.currentBluePrint) {
+ viewModel.currentBluePrint?.onDestroy()
}
+ val constraintSet =
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ val emptyLayout = ConstraintSet.Layout()
+ knownIds.forEach {
+ getConstraint(it).layout.copyFrom(emptyLayout)
+ }
+ blueprint.addViews(constraintLayout)
+ blueprint.applyConstraints(this)
+ applyTo(constraintLayout)
+ }
+ // Remove all unconstrained views.
+ constraintLayout.children
+ .filterNot { constraintSet.knownIds.contains(it.id) }
+ .forEach { constraintLayout.removeView(it) }
+
+ viewModel.currentBluePrint = blueprint
Trace.endSection()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 41c1c9600561..2cfc47845c71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -34,6 +34,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isInvisible
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
@@ -45,12 +46,12 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
@@ -109,6 +110,7 @@ constructor(
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardStateController: KeyguardStateController,
+ private val defaultShortcutsSection: DefaultShortcutsSection,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -119,6 +121,7 @@ constructor(
KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
false,
)
+
/** [shouldHideClock] here means that we never create and bind the clock views */
private val shouldHideClock: Boolean =
bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
@@ -176,7 +179,6 @@ constructor(
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
setupKeyguardRootView(rootView)
- setupShortcuts(rootView)
} else {
setUpBottomArea(rootView)
}
@@ -348,14 +350,14 @@ constructor(
FrameLayout.LayoutParams.MATCH_PARENT,
),
)
- KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
- keyguardBlueprintInteractor.refreshBlueprint()
+ setupShortcuts(keyguardRootView)
}
- private fun setupShortcuts(rootView: FrameLayout) {
+ private fun setupShortcuts(keyguardRootView: ConstraintLayout) {
+ defaultShortcutsSection.addShortcutViews(keyguardRootView)
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- rootView.requireViewById(R.id.start_button),
+ keyguardRootView.requireViewById(R.id.start_button),
quickAffordancesCombinedViewModel.startButton,
keyguardRootViewModel.alpha,
falsingManager,
@@ -367,7 +369,7 @@ constructor(
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- rootView.requireViewById(R.id.end_button),
+ keyguardRootView.requireViewById(R.id.end_button),
quickAffordancesCombinedViewModel.endButton,
keyguardRootViewModel.alpha,
falsingManager,
@@ -516,11 +518,11 @@ constructor(
// is dark or a light.
// TODO(b/277832214) we can potentially simplify this code by checking for
// wallpaperColors being null in the if clause above and removing the many ?.
- val wallpaperColorScheme =
- wallpaperColors?.let { ColorScheme(it, /* darkTheme= */ false) }
+ val wallpaperColorScheme = wallpaperColors?.let { ColorScheme(it, darkTheme = false) }
val lightClockColor = wallpaperColorScheme?.accent1?.s100
val darkClockColor = wallpaperColorScheme?.accent2?.s600
- /** Note that when [wallpaperColors] is null, isWallpaperDark is true. */
+
+ // Note that when [wallpaperColors] is null, isWallpaperDark is true.
val isWallpaperDark: Boolean =
(wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0
clock.events.onSeedColorChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 518df0719aaa..5a15fc236a0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -17,12 +17,15 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintLayout
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
@@ -39,24 +42,34 @@ import javax.inject.Inject
class DefaultKeyguardBlueprint
@Inject
constructor(
- private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
- private val defaultLockIconSection: DefaultLockIconSection,
- private val defaultShortcutsSection: DefaultShortcutsSection,
- private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
- private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
- private val defaultStatusViewSection: DefaultStatusViewSection,
- private val splitShadeGuidelines: SplitShadeGuidelines,
+ defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ defaultLockIconSection: DefaultLockIconSection,
+ defaultShortcutsSection: DefaultShortcutsSection,
+ defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+ defaultStatusViewSection: DefaultStatusViewSection,
+ defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
+ splitShadeGuidelines: SplitShadeGuidelines,
+ private val featureFlags: FeatureFlags,
) : KeyguardBlueprint {
override val id: String = DEFAULT
- override fun apply(constraintSet: ConstraintSet) {
- defaultIndicationAreaSection.apply(constraintSet)
- defaultLockIconSection.apply(constraintSet)
- defaultShortcutsSection.apply(constraintSet)
- defaultAmbientIndicationAreaSection.apply(constraintSet)
- defaultSettingsPopupMenuSection.apply(constraintSet)
- defaultStatusViewSection.apply(constraintSet)
- splitShadeGuidelines.apply(constraintSet)
+ override val sections =
+ arrayOf(
+ defaultIndicationAreaSection,
+ defaultLockIconSection,
+ defaultShortcutsSection,
+ defaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection,
+ defaultStatusViewSection,
+ defaultNotificationStackScrollLayoutSection,
+ splitShadeGuidelines,
+ )
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
+ super.addViews(constraintLayout)
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index 07f316b487bf..fda4c3d5376a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -18,7 +18,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
import com.android.systemui.communal.ui.view.layout.blueprints.DefaultCommunalBlueprint
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index 54c27960db3c..5ef625e62d00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -17,16 +17,14 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintSet
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
@@ -36,31 +34,28 @@ import javax.inject.Inject
class ShortcutsBesideUdfpsKeyguardBlueprint
@Inject
constructor(
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
- private val defaultLockIconSection: DefaultLockIconSection,
- private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
- private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
- private val alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
- private val defaultShortcutsSection: DefaultShortcutsSection,
- private val defaultStatusViewSection: DefaultStatusViewSection,
- private val splitShadeGuidelines: SplitShadeGuidelines,
+ defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ defaultLockIconSection: DefaultLockIconSection,
+ defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+ alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
+ defaultStatusViewSection: DefaultStatusViewSection,
+ splitShadeGuidelines: SplitShadeGuidelines,
+ defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
) : KeyguardBlueprint {
override val id: String = SHORTCUTS_BESIDE_UDFPS
- override fun apply(constraintSet: ConstraintSet) {
- defaultIndicationAreaSection.apply(constraintSet)
- defaultLockIconSection.apply(constraintSet)
- defaultAmbientIndicationAreaSection.apply(constraintSet)
- defaultSettingsPopupMenuSection.apply(constraintSet)
- if (keyguardUpdateMonitor.isUdfpsSupported) {
- alignShortcutsToUdfpsSection.apply(constraintSet)
- } else {
- defaultShortcutsSection.apply(constraintSet)
- }
- defaultStatusViewSection.apply(constraintSet)
- splitShadeGuidelines.apply(constraintSet)
- }
+ override val sections =
+ arrayOf(
+ defaultIndicationAreaSection,
+ defaultLockIconSection,
+ defaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection,
+ alignShortcutsToUdfpsSection,
+ defaultStatusViewSection,
+ defaultNotificationStackScrollLayoutSection,
+ splitShadeGuidelines,
+ )
companion object {
const val SHORTCUTS_BESIDE_UDFPS = "shortcutsBesideUdfps"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 156b9f3e5f48..587c6b7cb4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.LEFT
@@ -26,12 +27,58 @@ import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-class AlignShortcutsToUdfpsSection @Inject constructor(@Main private val resources: Resources) :
- KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+class AlignShortcutsToUdfpsSection
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val featureFlags: FeatureFlags,
+ private val keyguardQuickAffordancesCombinedViewModel:
+ KeyguardQuickAffordancesCombinedViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val falsingManager: FalsingManager,
+ private val indicationController: KeyguardIndicationController,
+ private val vibratorHelper: VibratorHelper,
+) : BaseShortcutsSection(), KeyguardSection {
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ addLeftShortcut(constraintLayout)
+ addRightShortcut(constraintLayout)
+ leftShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.start_button),
+ keyguardQuickAffordancesCombinedViewModel.startButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ rightShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.end_button),
+ keyguardQuickAffordancesCombinedViewModel.endButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt
new file mode 100644
index 000000000000..db0cf5ad3000
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.view.View
+import android.widget.ImageView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.content.res.ResourcesCompat
+import com.android.systemui.R
+import com.android.systemui.animation.view.LaunchableImageView
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+
+/** Base class for sections that add lockscreen shortcuts. */
+abstract class BaseShortcutsSection : KeyguardSection {
+ protected open var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
+ protected open var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {}
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {}
+
+ override fun onDestroy() {
+ leftShortcutHandle?.destroy()
+ rightShortcutHandle?.destroy()
+ }
+
+ protected open fun addLeftShortcut(constraintLayout: ConstraintLayout) {
+ if (constraintLayout.findViewById<View>(R.id.start_button) != null) return
+
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.start_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+
+ protected open fun addRightShortcut(constraintLayout: ConstraintLayout) {
+ if (constraintLayout.findViewById<View>(R.id.end_button) != null) return
+
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.end_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index abf25a23439f..f8455c53a51f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -17,7 +17,10 @@
package com.android.systemui.keyguard.ui.view.layout.sections
+import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
@@ -28,13 +31,44 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import javax.inject.Inject
class DefaultAmbientIndicationAreaSection
@Inject
-constructor(private val keyguardUpdateMonitor: KeyguardUpdateMonitor) : KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+constructor(
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val featureFlags: FeatureFlags,
+ private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+) : KeyguardSection {
+ private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (constraintLayout.findViewById<View>(R.id.ambient_indication_container) == null) {
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.ambient_indication, constraintLayout, false)
+
+ constraintLayout.addView(view)
+ }
+
+ ambientIndicationAreaHandle =
+ KeyguardAmbientIndicationAreaViewBinder.bind(
+ constraintLayout,
+ keyguardAmbientIndicationViewModel,
+ keyguardRootViewModel,
+ )
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(R.id.ambient_indication_container, MATCH_PARENT)
@@ -59,4 +93,8 @@ constructor(private val keyguardUpdateMonitor: KeyguardUpdateMonitor) : Keyguard
}
}
}
+
+ override fun onDestroy() {
+ ambientIndicationAreaHandle?.destroy()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index dee7ed570b05..f04bfc675f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -18,17 +18,53 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
+import android.view.View
import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
+import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.statusbar.KeyguardIndicationController
import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
-class DefaultIndicationAreaSection @Inject constructor(private val context: Context) :
- KeyguardSection {
+class DefaultIndicationAreaSection
+@Inject
+constructor(
+ private val context: Context,
+ private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val indicationController: KeyguardIndicationController,
+ private val featureFlags: FeatureFlags,
+) : KeyguardSection {
private val indicationAreaViewId = R.id.keyguard_indication_area
+ private var indicationAreaHandle: DisposableHandle? = null
- override fun apply(constraintSet: ConstraintSet) {
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (constraintLayout.findViewById<View>(indicationAreaViewId) == null) {
+ val view = KeyguardIndicationArea(context, null)
+ constraintLayout.addView(view)
+ }
+
+ indicationAreaHandle =
+ KeyguardIndicationAreaBinder.bind(
+ constraintLayout,
+ keyguardIndicationAreaViewModel,
+ keyguardRootViewModel,
+ indicationController,
+ featureFlags,
+ )
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(indicationAreaViewId, ViewGroup.LayoutParams.MATCH_PARENT)
constrainHeight(indicationAreaViewId, ViewGroup.LayoutParams.WRAP_CONTENT)
@@ -53,4 +89,8 @@ class DefaultIndicationAreaSection @Inject constructor(private val context: Cont
)
}
}
+
+ override fun onDestroy() {
+ indicationAreaHandle?.dispose()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 461faec217ca..3d62f3f1f985 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -21,13 +21,20 @@ import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.util.DisplayMetrics
+import android.view.View
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconView
+import com.android.keyguard.LockIconViewController
import com.android.systemui.R
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
class DefaultLockIconSection
@@ -37,16 +44,30 @@ constructor(
private val authController: AuthController,
private val windowManager: WindowManager,
private val context: Context,
+ private val notificationPanelView: NotificationPanelView,
+ private val featureFlags: FeatureFlags,
+ private val lockIconViewController: LockIconViewController,
) : KeyguardSection {
private val lockIconViewId = R.id.lock_icon_view
- override fun apply(constraintSet: ConstraintSet) {
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
+ notificationPanelView.removeView(it)
+ }
+ if (constraintLayout.findViewById<View>(R.id.lock_icon_view) == null) {
+ val view = LockIconView(context, null).apply { id = R.id.lock_icon_view }
+ constraintLayout.addView(view)
+ lockIconViewController.setLockIconView(view)
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
val scaleFactor: Float = authController.scaleFactor
val mBottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
- val mDefaultPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding)
- val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
val bounds = windowManager.currentWindowMetrics.bounds
val widthPixels = bounds.right.toFloat()
val heightPixels = bounds.bottom.toFloat()
@@ -57,12 +78,7 @@ constructor(
if (isUdfpsSupported) {
authController.udfpsLocation?.let { udfpsLocation ->
- centerLockIcon(
- udfpsLocation,
- authController.udfpsRadius,
- scaledPadding,
- constraintSet
- )
+ centerLockIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
}
} else {
centerLockIcon(
@@ -71,19 +87,13 @@ constructor(
(heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
),
lockIconRadiusPx * scaleFactor,
- scaledPadding,
constraintSet,
)
}
}
@VisibleForTesting
- internal fun centerLockIcon(
- center: Point,
- radius: Float,
- drawablePadding: Int,
- constraintSet: ConstraintSet
- ) {
+ internal fun centerLockIcon(center: Point, radius: Float, constraintSet: ConstraintSet) {
val sensorRect =
Rect().apply {
set(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
new file mode 100644
index 000000000000..a203e41db86e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import javax.inject.Inject
+
+class DefaultNotificationStackScrollLayoutSection
+@Inject
+constructor(
+ private val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val sharedNotificationContainer: SharedNotificationContainer,
+ private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val controller: NotificationStackScrollLayoutController,
+) : KeyguardSection {
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ // This moves the existing NSSL view to a different parent, as the controller is a
+ // singleton and recreating it has other bad side effects
+ notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ sharedNotificationContainer.addNotificationStackScrollLayout(it)
+ SharedNotificationContainerBinder.bind(
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ )
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
index ad1e4f8b98b4..660cc96f21e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -18,20 +18,65 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import android.view.LayoutInflater
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
+import androidx.core.view.isVisible
import com.android.systemui.R
+import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
-class DefaultSettingsPopupMenuSection @Inject constructor(@Main private val resources: Resources) :
- KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+class DefaultSettingsPopupMenuSection
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val featureFlags: FeatureFlags,
+ private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
+ private val vibratorHelper: VibratorHelper,
+ private val activityStarter: ActivityStarter,
+) : KeyguardSection {
+ private var settingsPopupMenuHandle: DisposableHandle? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (constraintLayout.findViewById<View?>(R.id.keyguard_settings_button) == null) {
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.keyguard_settings_popup_menu, constraintLayout, false)
+ .apply {
+ id = R.id.keyguard_settings_button
+ isVisible = false
+ alpha = 0f
+ } as LaunchableLinearLayout
+ constraintLayout.addView(view)
+ }
+
+ settingsPopupMenuHandle =
+ KeyguardSettingsViewBinder.bind(
+ constraintLayout.requireViewById<View>(R.id.keyguard_settings_button),
+ keyguardSettingsMenuViewModel,
+ vibratorHelper,
+ activityStarter,
+ )
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val horizontalOffsetMargin =
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
@@ -51,6 +96,11 @@ class DefaultSettingsPopupMenuSection @Inject constructor(@Main private val reso
BOTTOM,
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
)
+ setVisibility(R.id.keyguard_settings_button, View.GONE)
}
}
+
+ override fun onDestroy() {
+ settingsPopupMenuHandle?.dispose()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index db4653defd34..965910a1be66 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.LEFT
@@ -25,12 +26,58 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-class DefaultShortcutsSection @Inject constructor(@Main private val resources: Resources) :
- KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+class DefaultShortcutsSection
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val featureFlags: FeatureFlags,
+ private val keyguardQuickAffordancesCombinedViewModel:
+ KeyguardQuickAffordancesCombinedViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val falsingManager: FalsingManager,
+ private val indicationController: KeyguardIndicationController,
+ private val vibratorHelper: VibratorHelper,
+) : BaseShortcutsSection(), KeyguardSection {
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ addLeftShortcut(constraintLayout)
+ addRightShortcut(constraintLayout)
+ leftShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.start_button),
+ keyguardQuickAffordancesCombinedViewModel.startButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ rightShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.end_button),
+ keyguardQuickAffordancesCombinedViewModel.endButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
val horizontalOffsetMargin =
@@ -50,4 +97,15 @@ class DefaultShortcutsSection @Inject constructor(@Main private val resources: R
connect(R.id.end_button, BOTTOM, PARENT_ID, BOTTOM, verticalOffsetMargin)
}
}
+
+ /** Method to add shortcuts without applying any data binding. */
+ fun addShortcutViews(constraintLayout: ConstraintLayout) {
+ addLeftShortcut(constraintLayout)
+ addRightShortcut(constraintLayout)
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ applyConstraints(this)
+ applyTo(constraintLayout)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index f1f59732b121..321d7a70b087 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -18,23 +18,76 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.KeyguardStatusView
+import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.KeyguardViewConfigurator
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.Utils
+import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
-class DefaultStatusViewSection @Inject constructor(private val context: Context) : KeyguardSection {
+class DefaultStatusViewSection
+@Inject
+constructor(
+ private val context: Context,
+ private val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+ private val keyguardViewConfigurator: Lazy<KeyguardViewConfigurator>,
+ private val notificationPanelViewController: Lazy<NotificationPanelViewController>,
+ private val keyguardMediaController: KeyguardMediaController,
+) : KeyguardSection {
private val statusViewId = R.id.keyguard_status_view
- override fun apply(constraintSet: ConstraintSet) {
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
+ // Disable one of them
+ if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ notificationPanelView.findViewById<View>(statusViewId)?.let {
+ notificationPanelView.removeView(it)
+ }
+ if (constraintLayout.findViewById<View>(statusViewId) == null) {
+ val keyguardStatusView =
+ (LayoutInflater.from(context)
+ .inflate(R.layout.keyguard_status_view, constraintLayout, false)
+ as KeyguardStatusView)
+ .apply { clipChildren = false }
+
+ val statusViewComponent =
+ keyguardStatusViewComponentFactory.build(keyguardStatusView)
+ val controller = statusViewComponent.keyguardStatusViewController
+ controller.init()
+ constraintLayout.addView(keyguardStatusView)
+ keyguardMediaController.attachSplitShadeContainer(
+ keyguardStatusView.requireViewById<ViewGroup>(R.id.status_view_media_container)
+ )
+ keyguardViewConfigurator.get().keyguardStatusViewController = controller
+ notificationPanelViewController.get().updateStatusBarViewController()
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(statusViewId, MATCH_CONSTRAINT)
constrainHeight(statusViewId, WRAP_CONTENT)
@@ -52,4 +105,9 @@ class DefaultStatusViewSection @Inject constructor(private val context: Context)
setMargin(statusViewId, TOP, margin)
}
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override fun onDestroy() {
+ keyguardViewConfigurator.get().keyguardStatusViewController = null
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
index 668b17ffeba0..bd629d512614 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
@@ -18,23 +18,17 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
-import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.VERTICAL
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import javax.inject.Inject
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.VERTICAL
-class SplitShadeGuidelines @Inject constructor(private val context: Context) :
- KeyguardSection {
+class SplitShadeGuidelines @Inject constructor(private val context: Context) : KeyguardSection {
+ override fun addViews(constraintLayout: ConstraintLayout) {}
- override fun apply(constraintSet: ConstraintSet) {
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
// For use on large screens, it will provide a guideline vertically in the center to
// enable items to be aligned on the left or right sides
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
index 5e9e553616dc..e2bfc36aee37 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -17,13 +17,13 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import javax.inject.Inject
-@SysUISingleton
class KeyguardBlueprintViewModel
@Inject
constructor(keyguardBlueprintInteractor: KeyguardBlueprintInteractor) {
+ var currentBluePrint: KeyguardBlueprint? = null
val blueprint = keyguardBlueprintInteractor.blueprint
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 93c4902332c5..05c932372ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
@@ -30,7 +29,7 @@ class LockscreenSceneViewModel
@Inject
constructor(
authenticationInteractor: AuthenticationInteractor,
- private val bouncerInteractor: BouncerInteractor,
+ val longPress: KeyguardLongPressViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: Flow<SceneKey> =
@@ -41,9 +40,4 @@ constructor(
SceneKey.Bouncer
}
}
-
- /** Notifies that the lock button on the lock screen was clicked. */
- fun onLockButtonClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 8f884d24ad21..053c9b56ef96 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -206,6 +206,11 @@ class MediaProjectionAppSelectorActivity(
override fun bind(recentTasks: List<RecentTask>) {
recentsViewController.bind(recentTasks)
+ if (!hasWorkProfile()) {
+ // Make sure to refresh the adapter, to show/hide the recents view depending on whether
+ // there are recents or not.
+ mMultiProfilePagerAdapter.personalListAdapter.notifyDataSetChanged()
+ }
}
override fun returnSelectedApp(launchCookie: IBinder) {
@@ -248,9 +253,20 @@ class MediaProjectionAppSelectorActivity(
override fun shouldGetOnlyDefaultActivities() = false
- override fun shouldShowContentPreview() = true
+ override fun shouldShowContentPreview() =
+ if (hasWorkProfile()) {
+ // When the user has a work profile, we can always set this to true, and the layout is
+ // adjusted automatically, and hide the recents view.
+ true
+ } else {
+ // When there is no work profile, we should only show the content preview if there are
+ // recents, otherwise the collapsed app selector will look empty.
+ recentsViewController.hasRecentTasks
+ }
+
+ override fun shouldShowContentPreviewWhenEmpty() = shouldShowContentPreview()
- override fun shouldShowContentPreviewWhenEmpty(): Boolean = true
+ private fun hasWorkProfile() = mMultiProfilePagerAdapter.count > 1
override fun createMyUserIdProvider(): MyUserIdProvider =
object : MyUserIdProvider() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 398dcf260dff..38a6a8f3470b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -39,7 +39,6 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.R
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -59,6 +58,11 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.shared.system.SysUiStatsLog.SMARTSPACE_CARD_REPORTED
+import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD
+import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY as SSPACE_CARD_REPORTED__DREAM_OVERLAY
+import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN as SSPACE_CARD_REPORTED__LOCKSCREEN
+import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -100,7 +104,6 @@ constructor(
@Main executor: DelayableExecutor,
private val mediaManager: MediaDataManager,
configurationController: ConfigurationController,
- falsingCollector: FalsingCollector,
falsingManager: FalsingManager,
dumpManager: DumpManager,
private val logger: MediaUiEventLogger,
@@ -122,6 +125,7 @@ constructor(
/** Is the player currently visible (at the end of the transformation */
private var playersVisible: Boolean = false
+
/**
* The desired location where we'll be at the end of the transformation. Usually this matches
* the end location, except when we're still waiting on a state update call.
@@ -152,6 +156,7 @@ constructor(
@VisibleForTesting var mediaCarousel: MediaScrollView
val mediaCarouselScrollHandler: MediaCarouselScrollHandler
val mediaFrame: ViewGroup
+
@VisibleForTesting
lateinit var settingsButton: View
private set
@@ -280,7 +285,6 @@ constructor(
this::updatePageIndicatorLocation,
this::updateSeekbarListening,
this::closeGuts,
- falsingCollector,
falsingManager,
this::logSmartspaceImpression,
logger
@@ -327,23 +331,18 @@ constructor(
if (addOrUpdatePlayer(key, oldKey, data, isSsReactivated)) {
// Log card received if a new resumable media card is added
MediaPlayerData.getMediaPlayer(key)?.let {
- /* ktlint-disable max-line-length */
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = MediaPlayerData.getMediaPlayerIndex(key)
)
- /* ktlint-disable max-line-length */
}
if (
mediaCarouselScrollHandler.visibleToUser &&
@@ -362,24 +361,20 @@ constructor(
it.mUid + systemClock.currentTimeMillis().toInt()
)
it.mIsImpressed = false
- /* ktlint-disable max-line-length */
+
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = index,
receivedLatencyMillis = receivedSmartspaceCardLatency
)
- /* ktlint-disable max-line-length */
}
}
// If media container area already visible to the user, log impression for
@@ -431,19 +426,16 @@ constructor(
it.mUid + systemClock.currentTimeMillis().toInt()
)
it.mIsImpressed = false
- /* ktlint-disable max-line-length */
+
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = index,
receivedLatencyMillis =
@@ -451,25 +443,20 @@ constructor(
data.headphoneConnectionTimeMillis)
.toInt()
)
- /* ktlint-disable max-line-length */
}
}
}
addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
MediaPlayerData.getMediaPlayer(key)?.let {
- /* ktlint-disable max-line-length */
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = MediaPlayerData.getMediaPlayerIndex(key),
receivedLatencyMillis =
@@ -477,7 +464,6 @@ constructor(
data.headphoneConnectionTimeMillis)
.toInt()
)
- /* ktlint-disable max-line-length */
}
if (
mediaCarouselScrollHandler.visibleToUser &&
@@ -560,7 +546,10 @@ constructor(
mediaCarouselScrollHandler.onSettingsButtonUpdated(settings)
settingsButton.setOnClickListener {
logger.logCarouselSettings()
- activityStarter.startActivity(settingsIntent, true /* dismissShade */)
+ activityStarter.startActivity(
+ settingsIntent,
+ /* dismissShade= */ true,
+ )
}
}
@@ -1108,7 +1097,6 @@ constructor(
}
}
- @JvmOverloads
/**
* Log Smartspace events
*
@@ -1127,6 +1115,7 @@ constructor(
* between headphone connection to sysUI displays media recommendation card
* @param isSwipeToDismiss whether is to log swipe-to-dismiss event
*/
+ @JvmOverloads
fun logSmartspaceCardReported(
eventId: Int,
instanceId: Int,
@@ -1154,21 +1143,24 @@ constructor(
val cardinality = mediaContent.getChildCount()
surfaces.forEach { surface ->
- /* ktlint-disable max-line-length */
SysUiStatsLog.write(
- SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
+ SMARTSPACE_CARD_REPORTED,
eventId,
instanceId,
// Deprecated, replaced with AiAi feature type so we don't need to create logging
// card type for each new feature.
- SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
+ SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
surface,
// Use -1 as rank value to indicate user swipe to dismiss the card
if (isSwipeToDismiss) -1 else rank,
cardinality,
- if (mediaControlKey.isSsMediaRec) 15 // MEDIA_RECOMMENDATION
- else if (mediaControlKey.isSsReactivated) 43 // MEDIA_RESUME_SS_ACTIVATED
- else 31, // MEDIA_RESUME
+ if (mediaControlKey.isSsMediaRec) {
+ 15 // MEDIA_RECOMMENDATION
+ } else if (mediaControlKey.isSsReactivated) {
+ 43 // MEDIA_RESUME_SS_ACTIVATED
+ } else {
+ 31
+ }, // MEDIA_RESUME
uid,
interactedSubcardRank,
interactedSubcardCardinality,
@@ -1176,7 +1168,7 @@ constructor(
null, // Media cards cannot have subcards.
null // Media cards don't have dimensions today.
)
- /* ktlint-disable max-line-length */
+
if (DEBUG) {
Log.d(
TAG,
@@ -1259,6 +1251,7 @@ internal object MediaPlayerData {
instanceId = InstanceId.fakeInstanceId(-1),
appUid = -1
)
+
// Whether should prioritize Smartspace card.
internal var shouldPrioritizeSs: Boolean = false
private set
@@ -1291,6 +1284,7 @@ internal object MediaPlayerData {
private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
+
// A map that tracks order of visible media players before they get reordered.
private val visibleMediaPlayers = LinkedHashMap<String, MediaSortKey>()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index ce50a11cd85d..bbb61b4d1745 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -30,7 +30,6 @@ import com.android.settingslib.Utils
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
@@ -59,7 +58,6 @@ class MediaCarouselScrollHandler(
private var translationChangedListener: () -> Unit,
private var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit,
private val closeGuts: (immediate: Boolean) -> Unit,
- private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val logSmartspaceImpression: (Boolean) -> Unit,
private val logger: MediaUiEventLogger
@@ -67,8 +65,10 @@ class MediaCarouselScrollHandler(
/** Is the view in RTL */
val isRtl: Boolean
get() = scrollView.isLayoutRtl
+
/** Do we need falsing protection? */
var falsingProtectionNeeded: Boolean = false
+
/** The width of the carousel */
private var carouselWidth: Int = 0
@@ -80,6 +80,7 @@ class MediaCarouselScrollHandler(
/** The content where the players are added */
private var mediaContent: ViewGroup
+
/** The gesture detector to detect touch gestures */
private val gestureDetector: GestureDetectorCompat
@@ -140,9 +141,6 @@ class MediaCarouselScrollHandler(
) = onScroll(down!!, lastMotion, distanceX)
override fun onDown(e: MotionEvent): Boolean {
- if (falsingProtectionNeeded) {
- falsingCollector.onNotificationStartDismissing()
- }
return false
}
}
@@ -263,9 +261,6 @@ class MediaCarouselScrollHandler(
private fun onTouch(motionEvent: MotionEvent): Boolean {
val isUp = motionEvent.action == MotionEvent.ACTION_UP
- if (isUp && falsingProtectionNeeded) {
- falsingCollector.onNotificationStopDismissing()
- }
if (gestureDetector.onTouchEvent(motionEvent)) {
if (isUp) {
// If this is an up and we're flinging, we don't want to have this touch reach
@@ -482,8 +477,11 @@ class MediaCarouselScrollHandler(
}
val relativeLocation =
visibleMediaIndex.toFloat() +
- if (playerWidthPlusPadding > 0) scrollInAmount.toFloat() / playerWidthPlusPadding
- else 0f
+ if (playerWidthPlusPadding > 0) {
+ scrollInAmount.toFloat() / playerWidthPlusPadding
+ } else {
+ 0f
+ }
// Fix the location, because PageIndicator does not handle RTL internally
val location =
if (isRtl) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 64de9bd76122..5d732fb8ace9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -35,8 +35,8 @@ import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration
import javax.inject.Inject
/**
- * Controller that handles view of the recent apps selector in the media projection activity.
- * It is responsible for creating and updating recent apps view.
+ * Controller that handles view of the recent apps selector in the media projection activity. It is
+ * responsible for creating and updating recent apps view.
*/
@MediaProjectionAppSelectorScope
class MediaProjectionRecentsViewController
@@ -51,15 +51,21 @@ constructor(
private var views: Views? = null
private var lastBoundData: List<RecentTask>? = null
+ val hasRecentTasks: Boolean
+ get() = lastBoundData?.isNotEmpty() ?: false
+
init {
taskViewSizeProvider.addCallback(this)
}
fun createView(parent: ViewGroup): ViewGroup =
- views?.root ?: createRecentViews(parent).also {
- views = it
- lastBoundData?.let { recents -> bind(recents) }
- }.root
+ views?.root
+ ?: createRecentViews(parent)
+ .also {
+ views = it
+ lastBoundData?.let { recents -> bind(recents) }
+ }
+ .root
fun bind(recentTasks: List<RecentTask>) {
views?.apply {
@@ -88,7 +94,8 @@ constructor(
.inflate(R.layout.media_projection_recent_tasks, parent, /* attachToRoot= */ false)
as ViewGroup
- val container = recentsRoot.requireViewById<View>(R.id.media_projection_recent_tasks_container)
+ val container =
+ recentsRoot.requireViewById<View>(R.id.media_projection_recent_tasks_container)
container.setTaskHeightSize()
val progress = recentsRoot.requireViewById<View>(R.id.media_projection_recent_tasks_loader)
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index 809edc09070a..16885ed4b1d5 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -19,6 +19,7 @@ package com.android.systemui.power.domain.interactor
import android.os.PowerManager
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -34,7 +35,7 @@ class PowerInteractor
constructor(
private val repository: PowerRepository,
private val keyguardRepository: KeyguardRepository,
- private val falsingCollector: FalsingCollector,
+ @FalsingCollectorActual private val falsingCollector: FalsingCollector,
private val screenOffAnimationController: ScreenOffAnimationController,
private val statusBarStateController: StatusBarStateController,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 342c218c41e5..45ee7be35ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -168,7 +168,7 @@ constructor(
* Once a transition between one scene and another passes a threshold, the UI invokes this
* method to report it, updating the value in [desiredScene] to match what the UI shows.
*/
- internal fun onSceneChanged(scene: SceneModel, loggingReason: String) {
+ fun onSceneChanged(scene: SceneModel, loggingReason: String) {
updateDesiredScene(scene, loggingReason, logger::logSceneChangeCommitted)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 17470998cf74..7f77acc1789a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -14,11 +14,15 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.DisplayId
@@ -40,7 +44,12 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SE
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@@ -61,6 +70,7 @@ constructor(
private val sysUiState: SysUiState,
@DisplayId private val displayId: Int,
private val sceneLogger: SceneLogger,
+ @FalsingCollectorActual private val falsingCollector: FalsingCollector,
) : CoreStartable {
override fun start() {
@@ -69,6 +79,7 @@ constructor(
hydrateVisibility()
automaticallySwitchScenes()
hydrateSystemUiState()
+ collectFalsingSignals()
} else {
sceneLogger.logFrameworkEnabled(isEnabled = false)
}
@@ -225,6 +236,66 @@ constructor(
}
}
+ /** Collects and reports signals into the falsing system. */
+ private fun collectFalsingSignals() {
+ applicationScope.launch {
+ authenticationInteractor.isLockscreenDismissed.collect { isLockscreenDismissed ->
+ if (isLockscreenDismissed) {
+ falsingCollector.onSuccessfulUnlock()
+ }
+ }
+ }
+
+ applicationScope.launch {
+ keyguardInteractor.isDozing.distinctUntilChanged().collect { isDozing ->
+ falsingCollector.setShowingAod(isDozing)
+ }
+ }
+
+ applicationScope.launch {
+ keyguardInteractor.isAodAvailable
+ .flatMapLatest { isAodAvailable ->
+ if (!isAodAvailable) {
+ keyguardInteractor.wakefulnessModel
+ } else {
+ emptyFlow()
+ }
+ }
+ .map { wakefulnessModel ->
+ val wakeChange: Boolean? =
+ when (wakefulnessModel.state) {
+ WakefulnessState.STARTING_TO_WAKE -> true
+ WakefulnessState.ASLEEP -> false
+ else -> null
+ }
+ (wakeChange to wakefulnessModel.lastWakeReason).takeIf { wakeChange != null }
+ }
+ .filterNotNull()
+ .distinctUntilChangedBy { it.first }
+ .collect { (wakeChange, wakeReason) ->
+ when {
+ wakeChange == true && wakeReason.isTouch ->
+ falsingCollector.onScreenOnFromTouch()
+ wakeChange == true -> falsingCollector.onScreenTurningOn()
+ wakeChange == false -> falsingCollector.onScreenOff()
+ }
+ }
+ }
+
+ applicationScope.launch {
+ sceneInteractor.desiredScene
+ .map { it.key == SceneKey.Bouncer }
+ .distinctUntilChanged()
+ .collect { switchedToBouncerScene ->
+ if (switchedToBouncerScene) {
+ falsingCollector.onBouncerShown()
+ } else {
+ falsingCollector.onBouncerHidden()
+ }
+ }
+ }
+ }
+
private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
sceneInteractor.changeScene(
scene = SceneModel(targetSceneKey),
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 f9324a95c1e5..3e766073f720 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
@@ -110,6 +110,7 @@ object SceneWindowRootViewBinder {
// SysUI altogether.
private fun createVisibilityToggleView(otherView: View): View {
val toggleView = View(otherView.context)
+ otherView.isVisible = false
toggleView.layoutParams = FrameLayout.LayoutParams(200, 200, Gravity.CENTER_HORIZONTAL)
toggleView.setOnClickListener {
val now = Instant.now()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index c5b605ad7877..24316601830a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.ui.viewmodel
+import android.view.MotionEvent
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -30,7 +32,8 @@ import kotlinx.coroutines.flow.StateFlow
class SceneContainerViewModel
@Inject
constructor(
- private val interactor: SceneInteractor,
+ private val sceneInteractor: SceneInteractor,
+ private val falsingInteractor: FalsingInteractor,
) {
/**
* Keys of all scenes in the container.
@@ -38,17 +41,17 @@ constructor(
* The scenes will be sorted in z-order such that the last one is the one that should be
* rendered on top of all previous ones.
*/
- val allSceneKeys: List<SceneKey> = interactor.allSceneKeys()
+ val allSceneKeys: List<SceneKey> = sceneInteractor.allSceneKeys()
/** The scene that should be rendered. */
- val currentScene: StateFlow<SceneModel> = interactor.desiredScene
+ val currentScene: StateFlow<SceneModel> = sceneInteractor.desiredScene
/** Whether the container is visible. */
- val isVisible: StateFlow<Boolean> = interactor.isVisible
+ val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible
/** Notifies that the UI has transitioned sufficiently to the given scene. */
fun onSceneChanged(scene: SceneModel) {
- interactor.onSceneChanged(
+ sceneInteractor.onSceneChanged(
scene = scene,
loggingReason = SCENE_TRANSITION_LOGGING_REASON,
)
@@ -60,12 +63,27 @@ constructor(
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
- interactor.setTransitionState(transitionState)
+ sceneInteractor.setTransitionState(transitionState)
}
- /** Handles an event representing user input. */
- fun onUserInput() {
- interactor.onUserInput()
+ /**
+ * Notifies that a [MotionEvent] is first seen at the top of the scene container UI.
+ *
+ * Call this before the [MotionEvent] starts to propagate through the UI hierarchy.
+ */
+ fun onMotionEvent(event: MotionEvent) {
+ sceneInteractor.onUserInput()
+ falsingInteractor.onTouchEvent(event)
+ }
+
+ /**
+ * Notifies that a [MotionEvent] that was previously sent to [onMotionEvent] has passed through
+ * the scene container UI.
+ *
+ * Call this after the [MotionEvent] propagates completely through the UI hierarchy.
+ */
+ fun onMotionEventComplete() {
+ falsingInteractor.onMotionEventComplete()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index a41d6e8928ab..8db7abf7347f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1226,6 +1226,38 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private void updateViewControllers(
FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView) {
+ updateStatusBarViewController();
+ if (mKeyguardUserSwitcherController != null) {
+ // Try to close the switcher so that callbacks are triggered if necessary.
+ // Otherwise, NPV can get into a state where some of the views are still hidden
+ mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
+ }
+
+ mKeyguardQsUserSwitchController = null;
+ mKeyguardUserSwitcherController = null;
+
+ // Re-associate the KeyguardUserSwitcherController
+ if (userAvatarView != null) {
+ KeyguardQsUserSwitchComponent userSwitcherComponent =
+ mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
+ mKeyguardQsUserSwitchController =
+ userSwitcherComponent.getKeyguardQsUserSwitchController();
+ mKeyguardQsUserSwitchController.init();
+ mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
+ } else if (keyguardUserSwitcherView != null) {
+ KeyguardUserSwitcherComponent userSwitcherComponent =
+ mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
+ mKeyguardUserSwitcherController =
+ userSwitcherComponent.getKeyguardUserSwitcherController();
+ mKeyguardUserSwitcherController.init();
+ mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
+ } else {
+ mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false);
+ }
+ }
+
+ /** Updates the StatusBarViewController and updates any that depend on it. */
+ public void updateStatusBarViewController() {
// Re-associate the KeyguardStatusViewController
if (mKeyguardStatusViewController != null) {
mKeyguardStatusViewController.onDestroy();
@@ -1235,17 +1267,17 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// Need a shared controller until mKeyguardStatusViewController can be removed from
// here, due to important state being set in that controller. Rebind in order to pick
// up config changes
- mKeyguardViewConfigurator.bindKeyguardStatusView(mView);
mKeyguardStatusViewController =
mKeyguardViewConfigurator.getKeyguardStatusViewController();
} else {
KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById(
R.id.keyguard_status_view);
KeyguardStatusViewComponent statusViewComponent =
- mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
+ mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
}
+
mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -1256,34 +1288,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
});
updateClockAppearance();
-
- if (mKeyguardUserSwitcherController != null) {
- // Try to close the switcher so that callbacks are triggered if necessary.
- // Otherwise, NPV can get into a state where some of the views are still hidden
- mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
- }
-
- mKeyguardQsUserSwitchController = null;
- mKeyguardUserSwitcherController = null;
-
- // Re-associate the KeyguardUserSwitcherController
- if (userAvatarView != null) {
- KeyguardQsUserSwitchComponent userSwitcherComponent =
- mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
- mKeyguardQsUserSwitchController =
- userSwitcherComponent.getKeyguardQsUserSwitchController();
- mKeyguardQsUserSwitchController.init();
- mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
- } else if (keyguardUserSwitcherView != null) {
- KeyguardUserSwitcherComponent userSwitcherComponent =
- mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
- mKeyguardUserSwitcherController =
- userSwitcherComponent.getKeyguardUserSwitcherController();
- mKeyguardUserSwitcherController.init();
- mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
- } else {
- mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false);
- }
}
@Override
@@ -1403,7 +1407,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
updateViewControllers(userAvatarView, keyguardUserSwitcherView);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) && !mFeatureFlags.isEnabled(
+ Flags.LAZY_INFLATE_KEYGUARD)) {
attachSplitShadeMediaPlayerContainer(
mKeyguardViewConfigurator.getKeyguardRootView()
.findViewById(R.id.status_view_media_container));
@@ -2818,7 +2823,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void onTrackingStarted() {
- mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
endClosing();
mTracking = true;
mTrackingStartedListener.onTrackingStarted();
@@ -2834,7 +2838,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void onTrackingStopped(boolean expand) {
- mFalsingCollector.onTrackingStopped();
mTracking = false;
maybeStopTrackingExpansionFromStatusBar(expand);
@@ -2888,7 +2891,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@VisibleForTesting
void onUnlockHintStarted() {
- mFalsingCollector.onUnlockHintStarted();
mKeyguardIndicationController.showActionToUnlock();
mScrimController.setExpansionAffectsAlpha(false);
mNotificationStackScrollLayoutController.setUnlockHintRunning(true);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index ad9df72b0cdf..4a76dd0df47b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -21,7 +21,6 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import android.app.IActivityManager;
@@ -52,6 +51,7 @@ import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -76,6 +76,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -104,6 +105,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private final float mKeyguardMaxRefreshRate;
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardBypassController mKeyguardBypassController;
+ private final Executor mBackgroundExecutor;
private final AuthController mAuthController;
private ViewGroup mWindowRootView;
private LayoutParams mLp;
@@ -141,6 +143,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
ConfigurationController configurationController,
KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController,
+ @Background Executor backgroundExecutor,
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
KeyguardStateController keyguardStateController,
@@ -159,6 +162,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mLpChanged = new LayoutParams();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardBypassController = keyguardBypassController;
+ mBackgroundExecutor = backgroundExecutor;
mColorExtractor = colorExtractor;
mScreenOffAnimationController = screenOffAnimationController;
dumpManager.registerDumpable(this);
@@ -520,13 +524,14 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
applyWindowLayoutParams();
if (mHasTopUi != mHasTopUiChanged) {
- whitelistIpcs(() -> {
+ mHasTopUi = mHasTopUiChanged;
+ mBackgroundExecutor.execute(() -> {
try {
mActivityManager.setHasTopUi(mHasTopUiChanged);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call setHasTopUi", e);
}
- mHasTopUi = mHasTopUiChanged;
+
});
}
notifyStateChangedCallbacks();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 3a916cf198f3..54b806dd8d19 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -65,6 +65,8 @@ import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.DozeScrimController;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
@@ -118,6 +120,8 @@ public class NotificationShadeWindowViewController {
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarViewController mStatusBarViewController;
private final CentralSurfaces mService;
+ private final DozeServiceHost mDozeServiceHost;
+ private final DozeScrimController mDozeScrimController;
private final BackActionInteractor mBackActionInteractor;
private final PowerInteractor mPowerInteractor;
private final NotificationShadeWindowController mNotificationShadeWindowController;
@@ -152,6 +156,8 @@ public class NotificationShadeWindowViewController {
StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController,
CentralSurfaces centralSurfaces,
+ DozeServiceHost dozeServiceHost,
+ DozeScrimController dozeScrimController,
BackActionInteractor backActionInteractor,
PowerInteractor powerInteractor,
NotificationShadeWindowController controller,
@@ -187,8 +193,9 @@ public class NotificationShadeWindowViewController {
mLockIconViewController = lockIconViewController;
mBackActionInteractor = backActionInteractor;
mShadeLogger = shadeLogger;
- mLockIconViewController.init();
mService = centralSurfaces;
+ mDozeServiceHost = dozeServiceHost;
+ mDozeScrimController = dozeScrimController;
mPowerInteractor = powerInteractor;
mNotificationShadeWindowController = controller;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
@@ -226,6 +233,8 @@ public class NotificationShadeWindowViewController {
progressProvider -> progressProvider.addCallback(
mDisableSubpixelTextTransitionListener));
}
+
+ lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view));
}
/**
@@ -332,7 +341,7 @@ public class NotificationShadeWindowViewController {
}
if (mStatusBarStateController.isDozing()) {
- mService.extendDozePulse();
+ mDozeScrimController.extendPulse();
}
mLockIconViewController.onTouchEvent(
ev,
@@ -391,7 +400,7 @@ public class NotificationShadeWindowViewController {
@Override
public boolean shouldInterceptTouchEvent(MotionEvent ev) {
- if (mStatusBarStateController.isDozing() && !mService.isPulsing()
+ if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
&& !mDockManager.isDocked()) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mShadeLogger.d("NSWVC: capture all touch events in always-on");
@@ -445,7 +454,7 @@ public class NotificationShadeWindowViewController {
public boolean handleTouchEvent(MotionEvent ev) {
boolean handled = false;
if (mStatusBarStateController.isDozing()) {
- handled = !mService.isPulsing();
+ handled = !mDozeServiceHost.isPulsing();
}
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index c9c911b205e1..b2bbffdb5453 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -67,7 +67,6 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -147,7 +146,6 @@ public class QuickSettingsController implements Dumpable {
private final MediaHierarchyManager mMediaHierarchyManager;
private final AmbientState mAmbientState;
private final RecordingController mRecordingController;
- private final FalsingCollector mFalsingCollector;
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final ShadeLogger mShadeLog;
private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
@@ -335,7 +333,6 @@ public class QuickSettingsController implements Dumpable {
AmbientState ambientState,
RecordingController recordingController,
FalsingManager falsingManager,
- FalsingCollector falsingCollector,
AccessibilityManager accessibilityManager,
LockscreenGestureLogger lockscreenGestureLogger,
MetricsLogger metricsLogger,
@@ -381,7 +378,6 @@ public class QuickSettingsController implements Dumpable {
mAmbientState = ambientState;
mRecordingController = recordingController;
mFalsingManager = falsingManager;
- mFalsingCollector = falsingCollector;
mAccessibilityManager = accessibilityManager;
mLockscreenGestureLogger = lockscreenGestureLogger;
@@ -1660,7 +1656,6 @@ public class QuickSettingsController implements Dumpable {
}
}
if (shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
- mFalsingCollector.onQsDown();
mShadeLog.logMotionEvent(event,
"handleQsDown: down action, QS tracking enabled");
mTracking = true;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 05b1ac64a5f6..6585fcb1ae53 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -22,7 +22,6 @@ import android.os.Handler
import android.view.LayoutInflater
import android.view.ViewStub
import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.keyguard.LockIconView
import com.android.systemui.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -96,11 +95,11 @@ abstract class ShadeViewProviderModule {
?: throw IllegalStateException("Window root view could not be properly inflated")
}
- @Provides
- @SysUISingleton
// TODO(b/277762009): Do something similar to
// {@link StatusBarWindowModule.InternalWindowView} so that only
// {@link NotificationShadeWindowViewController} can inject this view.
+ @Provides
+ @SysUISingleton
fun providesNotificationShadeWindowView(
root: WindowRootView,
featureFlags: FeatureFlags,
@@ -206,21 +205,6 @@ abstract class ShadeViewProviderModule {
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
- fun providesLockIconView(
- keyguardRootView: KeyguardRootView,
- notificationPanelView: NotificationPanelView,
- featureFlags: FeatureFlags
- ): LockIconView {
- if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
- return keyguardRootView.requireViewById(R.id.lock_icon_view)
- } else {
- return notificationPanelView.requireViewById(R.id.lock_icon_view)
- }
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
fun providesTapAgainView(
notificationPanelView: NotificationPanelView,
): TapAgainView {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 25bb204d6f23..f004982413e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -81,6 +81,7 @@ class LockscreenShadeTransitionController @Inject constructor(
private val powerInteractor: PowerInteractor,
) : Dumpable {
private var pulseHeight: Float = 0f
+
@get:VisibleForTesting
var fractionToShade: Float = 0f
private set
@@ -255,17 +256,17 @@ class LockscreenShadeTransitionController @Inject constructor(
private fun updateResources() {
fullTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_full_transition_distance)
+ R.dimen.lockscreen_shade_full_transition_distance)
fullTransitionDistanceByTap = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_transition_by_tap_distance)
notificationShelfTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_notif_shelf_transition_distance)
+ R.dimen.lockscreen_shade_notif_shelf_transition_distance)
depthControllerTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_depth_controller_transition_distance)
+ R.dimen.lockscreen_shade_depth_controller_transition_distance)
udfpsTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_udfps_keyguard_transition_distance)
+ R.dimen.lockscreen_shade_udfps_keyguard_transition_distance)
statusBarTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_status_bar_transition_distance)
+ R.dimen.lockscreen_shade_status_bar_transition_distance)
useSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
}
@@ -292,8 +293,8 @@ class LockscreenShadeTransitionController @Inject constructor(
*/
internal fun canDragDown(): Boolean {
return (statusBarStateController.state == StatusBarState.KEYGUARD ||
- nsslController.isInLockedDownShade()) &&
- (qS.isFullyCollapsed || useSplitShade)
+ nsslController.isInLockedDownShade()) &&
+ (qS.isFullyCollapsed || useSplitShade)
}
/**
@@ -308,10 +309,15 @@ class LockscreenShadeTransitionController @Inject constructor(
if (nsslController.isInLockedDownShade()) {
logger.logDraggedDownLockDownShade(startingChild)
statusBarStateController.setLeaveOpenOnKeyguardHide(true)
- activityStarter.dismissKeyguardThenExecute(OnDismissAction {
- nextHideKeyguardNeedsNoAnimation = true
- false
- }, cancelRunnable, false /* afterKeyguardGone */)
+ activityStarter.dismissKeyguardThenExecute(
+ {
+ nextHideKeyguardNeedsNoAnimation = true
+ false
+ },
+ cancelRunnable,
+ /* afterKeyguardGone= */
+ false,
+ )
} else {
logger.logDraggedDown(startingChild, dragLengthY)
if (!ambientState.isDozing() || startingChild != null) {
@@ -320,11 +326,18 @@ class LockscreenShadeTransitionController @Inject constructor(
val animationHandler = { delay: Long ->
if (startingChild is ExpandableNotificationRow) {
startingChild.onExpandedByGesture(
- true /* drag down is always an open */)
+ true /* drag down is always an open */
+ )
}
shadeViewController.transitionToExpandedShade(delay)
- callbacks.forEach { it.setTransitionToFullShadeAmount(0f,
- true /* animated */, delay) }
+ callbacks.forEach {
+ it.setTransitionToFullShadeAmount(
+ 0f,
+ /* animated= */
+ true,
+ delay
+ )
+ }
// Let's reset ourselves, ready for the next animation
@@ -350,7 +363,12 @@ class LockscreenShadeTransitionController @Inject constructor(
*/
internal fun onDragDownReset() {
logger.logDragDownAborted()
- nsslController.setDimmed(true /* dimmed */, true /* animated */)
+ nsslController.setDimmed(
+ /* dimmed= */
+ true,
+ /* animate= */
+ true,
+ )
nsslController.resetScrollPosition()
nsslController.resetCheckSnoozeLeavebehind()
setDragDownAmountAnimated(0f)
@@ -361,7 +379,12 @@ class LockscreenShadeTransitionController @Inject constructor(
* @param above whether they dragged above it
*/
internal fun onCrossedThreshold(above: Boolean) {
- nsslController.setDimmed(!above /* dimmed */, true /* animate */)
+ nsslController.setDimmed(
+ /* dimmed= */
+ !above,
+ /* animate= */
+ true,
+ )
}
/**
@@ -411,8 +434,8 @@ class LockscreenShadeTransitionController @Inject constructor(
*/
internal val isDragDownAnywhereEnabled: Boolean
get() = (statusBarStateController.getState() == StatusBarState.KEYGUARD &&
- !keyguardBypassController.bypassEnabled &&
- (qS.isFullyCollapsed || useSplitShade))
+ !keyguardBypassController.bypassEnabled &&
+ (qS.isFullyCollapsed || useSplitShade))
/**
* The amount in pixels that the user has dragged down.
@@ -429,8 +452,15 @@ class LockscreenShadeTransitionController @Inject constructor(
qsTransitionController.dragDownAmount = value
- callbacks.forEach { it.setTransitionToFullShadeAmount(field,
- false /* animate */, 0 /* delay */) }
+ callbacks.forEach {
+ it.setTransitionToFullShadeAmount(
+ field,
+ /* animate= */
+ false,
+ /* delay= */
+ 0,
+ )
+ }
mediaHierarchyManager.setTransitionToFullShadeAmount(field)
scrimTransitionController.dragDownAmount = value
@@ -538,8 +568,11 @@ class LockscreenShadeTransitionController @Inject constructor(
shadeViewController.transitionToExpandedShade(delay)
}
}
- goToLockedShadeInternal(expandedView, animationHandler,
- cancelAction = null)
+ goToLockedShadeInternal(
+ expandedView,
+ animationHandler,
+ cancelAction = null
+ )
}
}
@@ -570,14 +603,19 @@ class LockscreenShadeTransitionController @Inject constructor(
var entry: NotificationEntry? = null
if (expandView is ExpandableNotificationRow) {
entry = expandView.entry
- entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */)
+ entry.setUserExpanded(
+ /* userExpanded= */
+ true,
+ /* allowChildExpansion= */
+ true,
+ )
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
entry.setGroupExpansionChanging(true)
userId = entry.sbn.userId
}
var fullShadeNeedsBouncer = (
- !lockScreenUserManager.shouldShowLockscreenNotifications() ||
+ !lockScreenUserManager.shouldShowLockscreenNotifications() ||
falsingCollector.shouldEnforceBouncer())
if (keyguardBypassController.bypassEnabled) {
fullShadeNeedsBouncer = false
@@ -596,7 +634,10 @@ class LockscreenShadeTransitionController @Inject constructor(
val cancelHandler = Runnable {
draggedDownEntry?.apply {
setUserLocked(false)
- notifyHeightChanged(false /* needsAnimation */)
+ notifyHeightChanged(
+ /* needsAnimation= */
+ false,
+ )
draggedDownEntry = null
}
cancelAction?.run()
@@ -614,7 +655,10 @@ class LockscreenShadeTransitionController @Inject constructor(
// This call needs to be after updating the shade state since otherwise
// the scrimstate resets too early
if (animationHandler != null) {
- animationHandler.invoke(0 /* delay */)
+ animationHandler.invoke(
+ /* delay= */
+ 0,
+ )
} else {
performDefaultGoToFullShadeAnimation(0)
}
@@ -721,12 +765,13 @@ class LockscreenShadeTransitionController @Inject constructor(
it.println("isDragDownAnywhereEnabled: $isDragDownAnywhereEnabled")
it.println("isFalsingCheckNeeded: $isFalsingCheckNeeded")
it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
- it.println("hasPendingHandlerOnKeyguardDismiss: " +
- "${animationHandlerOnKeyguardDismiss != null}")
+ it.println(
+ "hasPendingHandlerOnKeyguardDismiss: " +
+ "${animationHandlerOnKeyguardDismiss != null}"
+ )
}
}
-
fun addCallback(callback: Callback) {
if (!callbacks.contains(callback)) {
callbacks.add(callback)
@@ -791,7 +836,7 @@ class DragDownHelper(
fun updateResources(context: Context) {
minDragDistance = context.resources.getDimensionPixelSize(
- R.dimen.keyguard_drag_down_min_distance)
+ R.dimen.keyguard_drag_down_min_distance)
val configuration = ViewConfiguration.get(context)
touchSlop = configuration.scaledTouchSlop.toFloat()
slopMultiplier = configuration.scaledAmbiguousGestureMultiplier
@@ -808,16 +853,17 @@ class DragDownHelper(
initialTouchY = y
initialTouchX = x
}
+
MotionEvent.ACTION_MOVE -> {
val h = y - initialTouchY
// Adjust the touch slop if another gesture may be being performed.
val touchSlop = if (event.classification
- == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE)
+ == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE) {
touchSlop * slopMultiplier
- else
+ } else {
touchSlop
+ }
if (h > touchSlop && h > Math.abs(x - initialTouchX)) {
- falsingCollector.onNotificationStartDraggingDown()
isDraggingDown = true
captureStartingChild(initialTouchX, initialTouchY)
initialTouchY = y
@@ -858,8 +904,9 @@ class DragDownHelper(
}
return true
}
+
MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch &&
- dragDownCallback.canDragDown()) {
+ dragDownCallback.canDragDown()) {
dragDownCallback.onDraggedDown(startingChild, (y - initialTouchY).toInt())
if (startingChild != null) {
expandCallback.setUserLockedChild(startingChild, false)
@@ -870,6 +917,7 @@ class DragDownHelper(
stopDragging()
return false
}
+
MotionEvent.ACTION_CANCEL -> {
stopDragging()
return false
@@ -913,8 +961,8 @@ class DragDownHelper(
@VisibleForTesting
fun cancelChildExpansion(
- child: ExpandableView,
- animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
) {
if (child.actualHeight == child.collapsedHeight) {
expandCallback.setUserLockedChild(child, false)
@@ -936,7 +984,6 @@ class DragDownHelper(
}
private fun stopDragging() {
- falsingCollector.onNotificationStopDraggingDown()
if (startingChild != null) {
cancelChildExpansion(startingChild!!)
startingChild = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 763400b307fd..5bd40b8ed714 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -65,7 +65,6 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -74,6 +73,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Utils;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -87,8 +88,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
-import dagger.Lazy;
-
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
* notification, which this class keeps track of.
@@ -133,7 +132,6 @@ public class NotificationMediaManager implements Dumpable {
private final Context mContext;
private final ArrayList<MediaListener> mMediaListeners;
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final MediaArtworkProcessor mMediaArtworkProcessor;
private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
@@ -186,7 +184,6 @@ public class NotificationMediaManager implements Dumpable {
*/
public NotificationMediaManager(
Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -205,8 +202,6 @@ public class NotificationMediaManager implements Dumpable {
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
mMediaListeners = new ArrayList<>();
- // TODO: use KeyguardStateController#isOccluded to remove this dependency
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mVisibilityProvider = visibilityProvider;
mMainExecutor = mainExecutor;
@@ -619,9 +614,7 @@ public class NotificationMediaManager implements Dumpable {
NotificationShadeWindowController windowController =
mNotificationShadeWindowController.get();
- boolean hideBecauseOccluded =
- mCentralSurfacesOptionalLazy.get()
- .map(CentralSurfaces::isOccluded).orElse(false);
+ boolean hideBecauseOccluded = mKeyguardStateController.isOccluded();
final boolean hasArtwork = artworkDrawable != null;
mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index fc6eaa804f86..2c0741ecf986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -28,10 +28,10 @@ import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.ViewConfiguration
import androidx.annotation.VisibleForTesting
+import com.android.app.animation.Interpolators
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
-import com.android.app.animation.Interpolators
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
@@ -76,6 +76,7 @@ constructor(
companion object {
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
}
+
private val mPowerManager: PowerManager?
private var mInitialTouchX: Float = 0.0f
@@ -94,7 +95,10 @@ constructor(
pulseExpandAbortListener?.run()
}
}
- headsUpManager.unpinAll(true /* userUnPinned */)
+ headsUpManager.unpinAll(
+ /*userUnPinned= */
+ true,
+ )
}
}
var leavingLockscreen: Boolean = false
@@ -168,7 +172,6 @@ constructor(
MotionEvent.ACTION_MOVE -> {
val h = y - mInitialTouchY
if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) {
- falsingCollector.onStartExpandingFromPulse()
isExpanding = true
captureStartingChild(mInitialTouchX, mInitialTouchY)
mInitialTouchY = y
@@ -200,14 +203,14 @@ constructor(
event.action == MotionEvent.ACTION_UP) && isExpanding
val isDraggingNotificationOrCanBypass = mStartingChild?.showingPulsing() == true ||
- bypassController.canBypass()
+ bypassController.canBypass()
if ((!canHandleMotionEvent() || !isDraggingNotificationOrCanBypass) && !finishExpanding) {
// We allow cancellations/finishing to still go through here to clean up the state
return false
}
if (velocityTracker == null || !isExpanding ||
- event.actionMasked == MotionEvent.ACTION_DOWN) {
+ event.actionMasked == MotionEvent.ACTION_DOWN) {
return startExpansion(event)
}
velocityTracker!!.addMovement(event)
@@ -217,9 +220,12 @@ constructor(
when (event.actionMasked) {
MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance)
MotionEvent.ACTION_UP -> {
- velocityTracker!!.computeCurrentVelocity(1000 /* units */)
+ velocityTracker!!.computeCurrentVelocity(
+ /* units= */
+ 1000,
+ )
val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 &&
- statusBarStateController.state != StatusBarState.SHADE
+ statusBarStateController.state != StatusBarState.SHADE
if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
finishExpansion()
} else {
@@ -227,6 +233,7 @@ constructor(
}
recycleVelocityTracker()
}
+
MotionEvent.ACTION_CANCEL -> {
cancelExpansion()
recycleVelocityTracker()
@@ -243,17 +250,25 @@ constructor(
}
if (statusBarStateController.isDozing) {
wakeUpCoordinator.willWakeUp = true
- mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
- "com.android.systemui:PULSEDRAG")
+ mPowerManager!!.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:PULSEDRAG"
+ )
}
- lockscreenShadeTransitionController.goToLockedShade(startingChild,
- needsQSAnimation = false)
+ lockscreenShadeTransitionController.goToLockedShade(
+ startingChild,
+ needsQSAnimation = false
+ )
lockscreenShadeTransitionController.finishPulseAnimation(cancelled = false)
leavingLockscreen = true
isExpanding = false
if (mStartingChild is ExpandableNotificationRow) {
val row = mStartingChild as ExpandableNotificationRow?
- row!!.onExpandedByGesture(true /* userExpanded */)
+ row!!.onExpandedByGesture(
+ /*userExpanded= */
+ true,
+ )
}
}
@@ -261,15 +276,20 @@ constructor(
var expansionHeight = max(height, 0.0f)
if (mStartingChild != null) {
val child = mStartingChild!!
- val newHeight = Math.min((child.collapsedHeight + expansionHeight).toInt(),
- child.maxContentHeight)
+ val newHeight = Math.min(
+ (child.collapsedHeight + expansionHeight).toInt(),
+ child.maxContentHeight
+ )
child.actualHeight = newHeight
} else {
wakeUpCoordinator.setNotificationsVisibleForExpansion(
height
> lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications,
- true /* animate */,
- true /* increaseSpeed */)
+ /*animate= */
+ true,
+ /*increaseSpeed= */
+ true
+ )
}
lockscreenShadeTransitionController.setPulseHeight(expansionHeight, animate = false)
}
@@ -285,8 +305,8 @@ constructor(
@VisibleForTesting
fun reset(
- child: ExpandableView,
- animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
) {
if (child.actualHeight == child.collapsedHeight) {
setUserLocked(child, false)
@@ -315,15 +335,19 @@ constructor(
private fun cancelExpansion() {
isExpanding = false
- falsingCollector.onExpansionFromPulseStopped()
if (mStartingChild != null) {
reset(mStartingChild!!)
mStartingChild = null
}
lockscreenShadeTransitionController.finishPulseAnimation(cancelled = true)
- wakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */,
- true /* animate */,
- false /* increaseSpeed */)
+ wakeUpCoordinator.setNotificationsVisibleForExpansion(
+ /*visible= */
+ false,
+ /*animate= */
+ true,
+ /*increaseSpeed= */
+ false
+ )
}
private fun findView(x: Float, y: Float): ExpandableView? {
@@ -335,7 +359,9 @@ constructor(
val childAtRawPosition = stackScrollerController.getChildAtRawPosition(totalX, totalY)
return if (childAtRawPosition != null && childAtRawPosition.isContentExpandable) {
childAtRawPosition
- } else null
+ } else {
+ null
+ }
}
fun setUp(stackScrollerController: NotificationStackScrollLayoutController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index f726c4e8a753..3dfe068e6137 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -64,7 +64,6 @@ import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -135,7 +134,6 @@ public interface CentralSurfacesDependenciesModule {
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -152,7 +150,6 @@ public interface CentralSurfacesDependenciesModule {
DisplayManager displayManager) {
return new NotificationMediaManager(
context,
- centralSurfacesOptionalLazy,
notificationShadeWindowController,
visibilityProvider,
mediaArtworkProcessor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index d92d11b18d74..c02382dcde94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -74,7 +74,6 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.CallLayout;
import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.ViewRefactorFlag;
@@ -245,7 +244,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private NotificationEntry mEntry;
private String mAppName;
private FalsingManager mFalsingManager;
- private FalsingCollector mFalsingCollector;
/**
* Whether or not the notification is using the heads up view and should peek from the top.
@@ -1711,7 +1709,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
OnExpandClickListener onExpandClickListener,
CoordinateOnClickListener onFeedbackClickListener,
FalsingManager falsingManager,
- FalsingCollector falsingCollector,
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier,
OnUserInteractionCallback onUserInteractionCallback,
@@ -1743,7 +1740,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mOnExpandClickListener = onExpandClickListener;
setOnFeedbackClickListener(onFeedbackClickListener);
mFalsingManager = falsingManager;
- mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
for (NotificationContentView l : mLayouts) {
@@ -2471,7 +2467,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* @param allowChildExpansion whether a call to this method allows expanding children
*/
public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
- mFalsingCollector.setNotificationExpanded();
if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
&& !mChildrenContainer.showingAsLowPriority()) {
final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 80f5d1939ac0..af55f44b785a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -34,7 +34,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
@@ -102,7 +101,6 @@ public class ExpandableNotificationRowController implements NotifViewController
private final NotificationGutsManager mNotificationGutsManager;
private final OnUserInteractionCallback mOnUserInteractionCallback;
private final FalsingManager mFalsingManager;
- private final FalsingCollector mFalsingCollector;
private final FeatureFlags mFeatureFlags;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@@ -222,7 +220,6 @@ public class ExpandableNotificationRowController implements NotifViewController
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
OnUserInteractionCallback onUserInteractionCallback,
FalsingManager falsingManager,
- FalsingCollector falsingCollector,
FeatureFlags featureFlags,
PeopleNotificationIdentifier peopleNotificationIdentifier,
Optional<BubblesManager> bubblesManagerOptional,
@@ -251,7 +248,6 @@ public class ExpandableNotificationRowController implements NotifViewController
mFalsingManager = falsingManager;
mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
- mFalsingCollector = falsingCollector;
mFeatureFlags = featureFlags;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesManagerOptional = bubblesManagerOptional;
@@ -285,7 +281,6 @@ public class ExpandableNotificationRowController implements NotifViewController
mOnExpandClickListener,
mOnFeedbackClickListener,
mFalsingManager,
- mFalsingCollector,
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 6db8df91edcc..d8f513c493f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -455,7 +455,6 @@ public class NotificationStackScrollLayoutController {
@Override
public void onDragCancelled(View v) {
- mFalsingCollector.onNotificationStopDismissing();
}
/**
@@ -501,7 +500,6 @@ public class NotificationStackScrollLayoutController {
}
mView.addSwipedOutView(view);
- mFalsingCollector.onNotificationDismissed();
if (mFalsingCollector.shouldEnforceBouncer()) {
mActivityStarter.executeRunnableDismissingKeyguard(
null,
@@ -549,7 +547,6 @@ public class NotificationStackScrollLayoutController {
@Override
public void onBeginDrag(View v) {
- mFalsingCollector.onNotificationStartDismissing();
mView.onSwipeBegin(v);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index d0a093cf905a..cbb39150392c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -200,10 +200,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
void onKeyguardViewManagerStatesUpdated();
- boolean isPulsing();
-
- boolean isOccluded();
-
boolean isDeviceInVrMode();
NotificationPresenter getPresenter();
@@ -247,8 +243,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
@Deprecated
float getDisplayHeight();
- void readyForKeyguardDone();
-
void showKeyguard();
boolean hideKeyguard();
@@ -370,8 +364,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
@Deprecated
float getDisplayDensity();
- void extendDozePulse();
-
public static class KeyboardShortcutsMessage {
final int mDeviceId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index 72a5cfeaf052..10422e31dfaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -41,8 +41,6 @@ abstract class CentralSurfacesEmptyImpl : CentralSurfaces {
override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
override fun isLaunchingActivityOverLockscreen() = false
override fun onKeyguardViewManagerStatesUpdated() {}
- override fun isPulsing() = false
- override fun isOccluded() = false
override fun isDeviceInVrMode() = false
override fun getPresenter(): NotificationPresenter? = null
override fun onInputFocusTransfer(start: Boolean, cancel: Boolean, velocity: Float) {}
@@ -55,7 +53,6 @@ abstract class CentralSurfacesEmptyImpl : CentralSurfaces {
override fun dump(pwOriginal: PrintWriter, args: Array<String>) {}
override fun getDisplayWidth() = 0f
override fun getDisplayHeight() = 0f
- override fun readyForKeyguardDone() {}
override fun showKeyguard() {}
override fun hideKeyguard() = false
override fun showKeyguardImpl() {}
@@ -117,7 +114,6 @@ abstract class CentralSurfacesEmptyImpl : CentralSurfaces {
override fun setLaunchEmergencyActionOnFinishedWaking(launch: Boolean) {}
override fun getQSPanelController(): QSPanelController? = null
override fun getDisplayDensity() = 0f
- override fun extendDozePulse() {}
override fun setIsLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen: Boolean) {}
override fun getAnimatorControllerFromNotification(
associatedView: ExpandableNotificationRow?,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 4aa8b6bddb09..605b3d23be2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1480,7 +1480,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// - Shade is in QQS over keyguard - swiping up should take us back to keyguard
if (!isKeyguardShowing()
|| mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
- || isOccluded()
+ || mKeyguardStateController.isOccluded()
|| !mKeyguardStateController.canDismissLockScreen()
|| mKeyguardViewMediator.isAnySimPinSecure()
|| (mQsController.getExpanded() && trackingTouch)
@@ -1709,22 +1709,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public boolean isPulsing() {
- return mDozeServiceHost.isPulsing();
- }
-
- /**
- * When the keyguard is showing and covered by a "showWhenLocked" activity it
- * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
- *
- * @return whether the keyguard is currently occluded
- */
- @Override
- public boolean isOccluded() {
- return mKeyguardStateController.isOccluded();
- }
-
- @Override
public boolean isDeviceInVrMode() {
return mPresenter.isDeviceInVrMode();
}
@@ -2077,11 +2061,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return mDisplay.getRotation();
}
- @Override
- public void readyForKeyguardDone() {
- mStatusBarKeyguardViewManager.readyForKeyguardDone();
- }
-
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2325,11 +2304,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// there's no surface we can show to the user. Note that the device goes fully interactive
// late in the transition, so we also allow the device to start dozing once the screen has
// turned off fully.
+ boolean keyguardShowingUnOccluded =
+ mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded();
boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
&& (!mDeviceInteractive || (isGoingToSleep()
- && (isScreenFullyOff()
- || (mKeyguardStateController.isShowing() && !isOccluded()))));
- boolean isWakingAndOccluded = isOccluded() && isWakingOrAwake();
+ && (isScreenFullyOff() || keyguardShowingUnOccluded)));
+ boolean isWakingAndOccluded = mKeyguardStateController.isOccluded() && isWakingOrAwake();
boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
|| keyguardForDozing) && !wakeAndUnlocking && !isWakingAndOccluded;
if (keyguardForDozing) {
@@ -2852,7 +2832,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// cancelling a sleep), from the power button, on a device with a power button
// FPS, and 'press to unlock' is required.
mShouldDelayWakeUpAnimation =
- !isPulsing()
+ !mDozeServiceHost.isPulsing()
&& mStatusBarStateController.getDozeAmount() == 1f
&& mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON
@@ -3130,7 +3110,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if ((!isOccluded() || mShadeSurface.isPanelExpanded())
+ if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
&& (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
|| mTransitionToFullShadeProgress > 0f)) {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
@@ -3166,7 +3146,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// This will cancel the keyguardFadingAway animation if it is running. We need to do
// this as otherwise it can remain pending and leave keyguard in a weird state.
mUnlockScrimCallback.onCancelled();
- } else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) {
+ } else if (mKeyguardStateController.isShowing()
+ && !mKeyguardStateController.isOccluded()
+ && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
} else if (mKeyguardStateController.isShowing() && mKeyguardUpdateMonitor.isDreaming()
&& !unlocking) {
@@ -3403,11 +3385,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
- @Override
- public void extendDozePulse(){
- mDozeScrimController.extendPulse();
- }
-
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 801cdbf0e83e..4849f64659d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -185,7 +185,7 @@ public final class DozeServiceHost implements DozeHost {
return mDozingRequested;
}
- boolean isPulsing() {
+ public boolean isPulsing() {
return mPulsing;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 62a8cfde80da..b0f8276e460d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -763,7 +763,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
* Sets the amount of vertical over scroll that should be performed on the notifications scrim.
*/
public void setNotificationsOverScrollAmount(int overScrollAmount) {
- mNotificationsScrim.setTranslationY(overScrollAmount);
+ if (mNotificationsScrim != null) mNotificationsScrim.setTranslationY(overScrollAmount);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 3a9473085583..e1b608ffb1d6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -16,15 +16,16 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -45,7 +46,8 @@ import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 1acd676f02cd..93048a5787b2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -16,15 +16,16 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -52,7 +53,8 @@ import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPatternViewControllerTest : SysuiTestCase() {
private lateinit var mKeyguardPatternView: KeyguardPatternView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index efe1955595ca..2b90e7c66e8d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -22,16 +22,17 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
+import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -46,7 +47,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 80fd7213778e..61acacdb99a8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -16,16 +16,17 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -52,7 +53,8 @@ import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() {
@@ -175,7 +177,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
private fun getPinTopGuideline(): Float {
val cs = ConstraintSet()
- val container = objectKeyguardPINView.requireViewById(R.id.pin_container) as ConstraintLayout
+ val container =
+ objectKeyguardPINView.requireViewById(R.id.pin_container) as ConstraintLayout
cs.clone(container)
return cs.getConstraint(R.id.pin_pad_top_guideline).layout.guidePercent
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 80172a10062a..6bff4ce4aad9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -21,7 +21,6 @@ import android.content.res.Configuration
import android.hardware.biometrics.BiometricOverlayConstants
import android.media.AudioManager
import android.telephony.TelephonyManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.testing.TestableResources
import android.view.Gravity
@@ -30,6 +29,7 @@ import android.view.MotionEvent
import android.view.View
import android.view.WindowInsetsController
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
@@ -37,6 +37,7 @@ import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate
@@ -96,7 +97,8 @@ import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index f6450a4fb563..3e330d65329e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -44,7 +44,6 @@ import static org.mockito.Mockito.when;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Insets;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.View;
@@ -54,9 +53,11 @@ import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
+import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.plugins.FalsingManager;
@@ -75,7 +76,8 @@ import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 64e1458c42d4..68c2f59722ad 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -25,17 +25,18 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.WindowInsetsController;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
+import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
@@ -49,7 +50,8 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 291dda256c4f..4db5f35ca07d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -18,13 +18,14 @@ package com.android.keyguard
import android.telephony.PinResult
import android.telephony.TelephonyManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -43,7 +44,8 @@ import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardSimPinViewControllerTest : SysuiTestCase() {
private lateinit var simPinView: KeyguardSimPinView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 626faa601970..47ff3b90e2e2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -18,13 +18,14 @@ package com.android.keyguard
import android.telephony.PinResult
import android.telephony.TelephonyManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -39,7 +40,8 @@ import org.mockito.Mockito
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardSimPukViewControllerTest : SysuiTestCase() {
private lateinit var simPukView: KeyguardSimPukView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index b9e3f140a9ab..09ff546120c6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -24,7 +24,6 @@ import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -148,7 +147,6 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
mUnderTest = new LockIconViewController(
- mLockIconView,
mStatusBarStateController,
mKeyguardUpdateMonitor,
mKeyguardViewController,
@@ -167,7 +165,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
.getKeyguardTransitionInteractor(),
KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags,
- mPrimaryBouncerInteractor
+ mPrimaryBouncerInteractor,
+ mContext
);
}
@@ -228,9 +227,6 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
protected void init(boolean useMigrationFlag) {
mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag);
- mUnderTest.init();
-
- verify(mLockIconView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture());
- mAttachCaptor.getValue().onViewAttachedToWindow(mLockIconView);
+ mUnderTest.setLockIconView(mLockIconView);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 45021ba1b300..979fc83ec43a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -52,6 +52,12 @@ import org.junit.runner.RunWith;
@TestableLooper.RunWithLooper
public class LockIconViewControllerTest extends LockIconViewControllerBaseTest {
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ when(mLockIconView.isAttachedToWindow()).thenReturn(true);
+ }
+
@Test
public void testUpdateFingerprintLocationOnInit() {
// GIVEN fp sensor location is available pre-attached
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 937a7a92ec46..037c1badf0ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.eq
@@ -55,6 +56,8 @@ class CameraGestureHelperTest : SysuiTestCase() {
@Mock
lateinit var centralSurfaces: CentralSurfaces
@Mock
+ lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
lateinit var keyguardStateController: KeyguardStateController
@Mock
lateinit var packageManager: PackageManager
@@ -91,6 +94,7 @@ class CameraGestureHelperTest : SysuiTestCase() {
context = mock(),
centralSurfaces = centralSurfaces,
keyguardStateController = keyguardStateController,
+ statusBarKeyguardViewManager = statusBarKeyguardViewManager,
packageManager = packageManager,
activityManager = activityManager,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index e3a75f161318..4ad9549d1d4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -2,6 +2,7 @@ package com.android.systemui.communal.ui.view.layout.blueprints
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -28,9 +29,16 @@ class DefaultCommunalBlueprintTest : SysuiTestCase() {
}
@Test
- fun apply() {
+ fun addView() {
+ val constraintLayout = ConstraintLayout(context, null)
+ blueprint.addViews(constraintLayout)
+ verify(widgetSection).addViews(constraintLayout)
+ }
+
+ @Test
+ fun applyConstraints() {
val cs = ConstraintSet()
- blueprint.apply(cs)
- verify(widgetSection).apply(cs)
+ blueprint.applyConstraints(cs)
+ verify(widgetSection).applyConstraints(cs)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index b1061ba76c14..74d0d210b9b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -36,7 +36,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.APP_PANELS_ALL_APPS_ALLOWED
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.ActivityTaskManagerProxy
import com.android.systemui.util.concurrency.FakeExecutor
@@ -123,9 +122,6 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
arrayOf(componentName.packageName)
)
- // Return false by default, we'll test the true path
- `when`(featureFlags.isEnabled(APP_PANELS_ALL_APPS_ALLOWED)).thenReturn(false)
-
val wrapper = object : ContextWrapper(mContext) {
override fun createContextAsUser(user: UserHandle, flags: Int): Context {
return baseContext
@@ -469,38 +465,7 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
}
@Test
- fun testPackageNotPreferred_nullPanel() {
- mContext.orCreateTestableResources
- .addOverride(R.array.config_controlsPreferredPackages, arrayOf<String>())
-
- val serviceInfo = ServiceInfo(
- componentName,
- activityName
- )
-
- `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
- .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
-
- setUpQueryResult(listOf(
- ActivityInfo(
- activityName,
- exported = true,
- permission = Manifest.permission.BIND_CONTROLS
- )
- ))
-
- val list = listOf(serviceInfo)
- serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
- executor.runAllReady()
-
- assertNull(controller.getCurrentServices()[0].panelActivity)
- }
-
- @Test
- fun testPackageNotPreferred_allowAllApps_correctPanel() {
- `when`(featureFlags.isEnabled(APP_PANELS_ALL_APPS_ALLOWED)).thenReturn(true)
-
+ fun testPackageNotPreferred_correctPanel() {
mContext.orCreateTestableResources
.addOverride(R.array.config_controlsPreferredPackages, arrayOf<String>())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
index ccd631ec37d0..8f6634478617 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.graphics.drawable.VectorDrawable
import android.net.Uri
+import android.util.Size
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -78,12 +79,19 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
- fun invalidIcon_returnsNull() =
+ fun invalidIcon_loadDrawable_returnsNull() =
testScope.runTest {
assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull()
}
@Test
+ fun invalidIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(imageLoader.loadSize(Icon.createWithFilePath("this is broken"), context))
+ .isNull()
+ }
+
+ @Test
fun invalidIS_returnsNull() =
testScope.runTest {
assertThat(
@@ -172,6 +180,17 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validBitmapIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ assertThat(imageLoader.loadSize(Icon.createWithBitmap(bitmap), context)).isNull()
+ }
+
+ @Test
fun validUriIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -186,6 +205,17 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validUriIcon_returnsSize() =
+ testScope.runTest {
+ val drawable = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
+ val uri =
+ "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+ val loadedSize =
+ imageLoader.loadSize(Icon.createWithContentUri(Uri.parse(uri)), context)
+ assertSizeEqualToDrawableSize(loadedSize, drawable)
+ }
+
+ @Test
fun validDataIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -205,6 +235,54 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validDataIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ val bos =
+ ByteArrayOutputStream(
+ bitmap.byteCount * 2
+ ) // Compressed bitmap should be smaller than its source.
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
+
+ val array = bos.toByteArray()
+ assertThat(imageLoader.loadSize(Icon.createWithData(array, 0, array.size), context))
+ .isNull()
+ }
+
+ @Test
+ fun validResourceIcon_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ Icon.createWithResource(
+ "com.android.systemui.tests",
+ R.drawable.dessert_zombiegingerbread
+ )
+ )
+ assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
+ }
+
+ @Test
+ fun validResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadSize(
+ Icon.createWithResource(
+ "com.android.systemui.tests",
+ R.drawable.dessert_zombiegingerbread
+ ),
+ context
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun validSystemResourceIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -217,6 +295,18 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validSystemResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadSize(
+ Icon.createWithResource("android", android.R.drawable.ic_dialog_alert),
+ context
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun invalidDifferentPackageResourceIcon_returnsNull() =
testScope.runTest {
val loadedDrawable =
@@ -230,6 +320,20 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun invalidDifferentPackageResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadDrawable(
+ Icon.createWithResource(
+ "noooope.wrong.package",
+ R.drawable.dessert_zombiegingerbread
+ )
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() =
testScope.runTest {
val loadedDrawable =
@@ -343,4 +447,10 @@ class ImageLoaderTest : SysuiTestCase() {
assertThat(actual?.width).isEqualTo(expected.width)
assertThat(actual?.height).isEqualTo(expected.height)
}
+
+ private fun assertSizeEqualToDrawableSize(actual: Size?, expected: Drawable) {
+ assertThat(actual).isNotNull()
+ assertThat(actual?.width).isEqualTo(expected.intrinsicWidth)
+ assertThat(actual?.height).isEqualTo(expected.intrinsicHeight)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
new file mode 100644
index 000000000000..71a56cd8588c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.haptics.slider
+
+import android.widget.SeekBar
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import junit.framework.Assert.assertEquals
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SeekableSliderEventProducerTest : SysuiTestCase() {
+
+ private val seekBar = SeekBar(mContext)
+ private val eventProducer = SeekableSliderEventProducer()
+ private val eventFlow = eventProducer.produceEvents()
+
+ @Test
+ fun onStartTrackingTouch_noProgress_trackingTouchEventProduced() = runTest {
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onStartTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, 0F), latest)
+ }
+
+ @Test
+ fun onStopTrackingTouch_noProgress_StoppedTrackingTouchEventProduced() = runTest {
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onStopTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByUser_changeByUserEventProduced_withNormalizedProgress() = runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, 0.5F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByUser_zeroWidthSlider_changeByUserEventProduced_withMaxProgress() =
+ runTest {
+ // No-width slider where the min and max values are the same
+ seekBar.min = 100
+ seekBar.max = 100
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, 1.0F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_changeByProgramEventProduced_withNormalizedProgress() = runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, false)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, 0.5F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_zeroWidthSlider_changeByProgramEventProduced_withMaxProgress() =
+ runTest {
+ // No-width slider where the min and max values are the same
+ seekBar.min = 100
+ seekBar.max = 100
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, false)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, 1.0F), latest)
+ }
+
+ @Test
+ fun onStartTrackingTouch_afterProgress_trackingTouchEventProduced_withNormalizedProgress() =
+ runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+ eventProducer.onStartTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, 0.5F), latest)
+ }
+
+ @Test
+ fun onStopTrackingTouch_afterProgress_stopTrackingTouchEventProduced_withNormalizedProgress() =
+ runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+ eventProducer.onStopTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5F), latest)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index daafba24e3ca..f78d051eca61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -221,6 +221,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mSystemClock = new FakeSystemClock();
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
+ when(mPowerManager.isInteractive()).thenReturn(true);
when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
@@ -241,6 +242,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mConfigurationController,
mViewMediator,
mKeyguardBypassController,
+ mUiBgExecutor,
mColorExtractor,
mDumpManager,
mKeyguardStateController,
@@ -868,8 +870,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
when(mKeyguardStateController.isShowing()).thenReturn(true);
TestableLooper.get(this).processAllMessages();
- when(mPowerManager.isInteractive()).thenReturn(true);
-
mViewMediator.onSystemReady();
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index addb1815cead..3b4eab2a5fe1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -19,13 +19,17 @@ package com.android.systemui.keyguard.ui.view.layout.blueprints
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
@@ -34,6 +38,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -50,7 +55,9 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
@Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
@Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection
+ @Mock private lateinit var defaultNSSLSection: DefaultNotificationStackScrollLayoutSection
@Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines
+ private val featureFlags = FakeFeatureFlags()
@Before
fun setup() {
@@ -64,20 +71,32 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
+ defaultNSSLSection,
splitShadeGuidelines,
+ featureFlags,
)
+ featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, false)
}
@Test
- fun apply() {
+ fun addViews() {
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ underTest.sections.forEach { verify(it, never()).addViews(constraintLayout) }
+ }
+
+ @Test
+ fun addViews_lazyInflateFlagOn() {
+ featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ underTest.sections.forEach { verify(it).addViews(constraintLayout) }
+ }
+
+ @Test
+ fun applyConstraints() {
val cs = ConstraintSet()
- underTest.apply(cs)
- verify(defaultIndicationAreaSection).apply(cs)
- verify(defaultLockIconSection).apply(cs)
- verify(defaultShortcutsSection).apply(cs)
- verify(defaultAmbientIndicationAreaSection).apply(cs)
- verify(defaultSettingsPopupMenuSection).apply(cs)
- verify(defaultStatusViewSection).apply(cs)
- verify(splitShadeGuidelines).apply(cs)
+ underTest.applyConstraints(cs)
+ underTest.sections.forEach { verify(it).applyConstraints(cs) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 3dcc03d61fd7..798b23e4a074 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -22,20 +22,45 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.statusbar.KeyguardIndicationController
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
@RunWith(JUnit4::class)
@SmallTest
class DefaultIndicationAreaSectionTest : SysuiTestCase() {
- private val underTest = DefaultIndicationAreaSection(context)
+ @Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel
+ @Mock private lateinit var keyguardRootViewModel: KeyguardRootViewModel
+ @Mock private lateinit var indicationController: KeyguardIndicationController
+ @Mock private lateinit var featureFlags: FeatureFlags
+
+ private lateinit var underTest: DefaultIndicationAreaSection
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ DefaultIndicationAreaSection(
+ context,
+ keyguardIndicationAreaViewModel,
+ keyguardRootViewModel,
+ indicationController,
+ featureFlags,
+ )
+ }
@Test
fun apply() {
val cs = ConstraintSet()
- underTest.apply(cs)
+ underTest.applyConstraints(cs)
val constraint = cs.getConstraint(R.id.keyguard_indication_area)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
index 379c03c4353d..1192a8017dc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
@@ -22,10 +22,12 @@ import android.view.WindowManager
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconViewController
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shade.NotificationPanelView
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -41,19 +43,30 @@ class DefaultLockIconSectionTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var authController: AuthController
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
+ @Mock private lateinit var notificationPanelView: NotificationPanelView
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var lockIconViewController: LockIconViewController
private lateinit var underTest: DefaultLockIconSection
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest =
- DefaultLockIconSection(keyguardUpdateMonitor, authController, windowManager, context)
+ DefaultLockIconSection(
+ keyguardUpdateMonitor,
+ authController,
+ windowManager,
+ context,
+ notificationPanelView,
+ featureFlags,
+ lockIconViewController
+ )
}
@Test
fun apply() {
val cs = ConstraintSet()
- underTest.apply(cs)
+ underTest.applyConstraints(cs)
val constraint = cs.getConstraint(R.id.lock_icon_view)
@@ -64,7 +77,7 @@ class DefaultLockIconSectionTest : SysuiTestCase() {
@Test
fun testCenterLockIcon() {
val cs = ConstraintSet()
- underTest.centerLockIcon(Point(5, 6), 1F, 5, cs)
+ underTest.centerLockIcon(Point(5, 6), 1F, cs)
val constraint = cs.getConstraint(R.id.lock_icon_view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index a9f288d3575f..b30dc9cc67c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -23,15 +23,13 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class LockscreenSceneViewModelTest : SysuiTestCase() {
@@ -47,10 +45,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
private val underTest =
LockscreenSceneViewModel(
authenticationInteractor = authenticationInteractor,
- bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
+ longPress =
+ KeyguardLongPressViewModel(
+ interactor = mock(),
),
)
@@ -76,30 +73,4 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
}
-
- @Test
- fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- runCurrent()
-
- underTest.onLockButtonClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- }
-
- @Test
- fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
- runCurrent()
-
- underTest.onLockButtonClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index ef51e474bf71..3961a945414d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -33,7 +33,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.TestScopeProvider
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -105,7 +104,6 @@ class MediaCarouselControllerTest : SysuiTestCase() {
@Mock @Main private lateinit var executor: DelayableExecutor
@Mock lateinit var mediaDataManager: MediaDataManager
@Mock lateinit var configurationController: ConfigurationController
- @Mock lateinit var falsingCollector: FalsingCollector
@Mock lateinit var falsingManager: FalsingManager
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var logger: MediaUiEventLogger
@@ -146,7 +144,6 @@ class MediaCarouselControllerTest : SysuiTestCase() {
executor,
mediaDataManager,
configurationController,
- falsingCollector,
falsingManager,
dumpManager,
logger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 0ecbcdebc68c..6006cd42424a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.model.SysUiState
import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
@@ -99,7 +100,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
)
private val sceneContainerViewModel =
SceneContainerViewModel(
- interactor = sceneInteractor,
+ sceneInteractor = sceneInteractor,
+ falsingInteractor = utils.falsingInteractor(),
)
.apply { setTransitionState(transitionState) }
@@ -117,7 +119,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
authenticationInteractor = authenticationInteractor,
- bouncerInteractor = bouncerInteractor,
+ longPress =
+ KeyguardLongPressViewModel(
+ interactor = mock(),
+ ),
)
private val shadeSceneViewModel =
@@ -151,6 +156,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
sysUiState = sysUiState,
displayId = displayTracker.defaultDisplayId,
sceneLogger = mock(),
+ falsingCollector = utils.falsingCollector(),
)
startable.start()
@@ -165,9 +171,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
- lockscreenSceneViewModel.onLockButtonClicked()
- assertCurrentScene(SceneKey.Bouncer)
- emulateUiSceneTransition()
+ emulateUserDrivenTransition(SceneKey.Bouncer)
enterPin()
assertCurrentScene(SceneKey.Gone)
@@ -460,10 +464,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
.that(authenticationInteractor.isUnlocked.value)
.isFalse()
- lockscreenSceneViewModel.onLockButtonClicked()
- runCurrent()
- emulateUiSceneTransition()
-
+ emulateUserDrivenTransition(SceneKey.Bouncer)
enterPin()
emulateUiSceneTransition(
expectedVisible = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index d60d99430860..771c3e330e8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -22,6 +22,7 @@ import android.view.Display
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -45,6 +46,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -60,6 +62,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
private val authenticationInteractor =
utils.authenticationInteractor(
repository = authenticationRepository,
+ sceneInteractor = sceneInteractor,
)
private val keyguardRepository = utils.keyguardRepository
private val keyguardInteractor =
@@ -67,6 +70,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
repository = keyguardRepository,
)
private val sysUiState: SysUiState = mock()
+ private val falsingCollector: FalsingCollector = mock()
private val underTest =
SceneContainerStartable(
@@ -78,6 +82,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
sysUiState = sysUiState,
displayId = Display.DEFAULT_DISPLAY,
sceneLogger = mock(),
+ falsingCollector = falsingCollector,
)
@Test
@@ -243,7 +248,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -259,7 +264,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -275,7 +280,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -294,11 +299,217 @@ class SceneContainerStartableTest : SysuiTestCase() {
authenticationRepository.setUnlocked(true)
runCurrent()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
+ @Test
+ fun collectFalsingSignals_onSuccessfulUnlock() =
+ testScope.runTest {
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector, never()).onSuccessfulUnlock()
+
+ // Move around scenes without unlocking.
+ listOf(
+ SceneKey.Shade,
+ SceneKey.QuickSettings,
+ SceneKey.Shade,
+ SceneKey.Lockscreen,
+ SceneKey.Bouncer,
+ )
+ .forEach { sceneKey ->
+ sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
+ runCurrent()
+ verify(falsingCollector, never()).onSuccessfulUnlock()
+ }
+
+ // Changing to the Gone scene should report a successful unlock.
+ sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+ runCurrent()
+ verify(falsingCollector).onSuccessfulUnlock()
+
+ // Move around scenes without changing back to Lockscreen, shouldn't report another
+ // unlock.
+ listOf(
+ SceneKey.Shade,
+ SceneKey.QuickSettings,
+ SceneKey.Shade,
+ SceneKey.Gone,
+ )
+ .forEach { sceneKey ->
+ sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
+ runCurrent()
+ verify(falsingCollector, times(1)).onSuccessfulUnlock()
+ }
+
+ // Changing to the Lockscreen scene shouldn't report a successful unlock.
+ sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
+ runCurrent()
+ verify(falsingCollector, times(1)).onSuccessfulUnlock()
+
+ // Move around scenes without unlocking.
+ listOf(
+ SceneKey.Shade,
+ SceneKey.QuickSettings,
+ SceneKey.Shade,
+ SceneKey.Lockscreen,
+ SceneKey.Bouncer,
+ )
+ .forEach { sceneKey ->
+ sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
+ runCurrent()
+ verify(falsingCollector, times(1)).onSuccessfulUnlock()
+ }
+
+ // Changing to the Gone scene should report a second successful unlock.
+ sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+ runCurrent()
+ verify(falsingCollector, times(2)).onSuccessfulUnlock()
+ }
+
+ @Test
+ fun collectFalsingSignals_setShowingAod() =
+ testScope.runTest {
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector).setShowingAod(false)
+
+ keyguardRepository.setIsDozing(true)
+ runCurrent()
+ verify(falsingCollector).setShowingAod(true)
+
+ keyguardRepository.setIsDozing(false)
+ runCurrent()
+ verify(falsingCollector, times(2)).setShowingAod(false)
+ }
+
+ @Test
+ fun collectFalsingSignals_screenOnAndOff_aodUnavailable() =
+ testScope.runTest {
+ keyguardRepository.setAodAvailable(false)
+ runCurrent()
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, times(1)).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, times(1)).onScreenOnFromTouch()
+ verify(falsingCollector, times(1)).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, times(1)).onScreenOnFromTouch()
+ verify(falsingCollector, times(2)).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, times(2)).onScreenTurningOn()
+ verify(falsingCollector, times(1)).onScreenOnFromTouch()
+ verify(falsingCollector, times(2)).onScreenOff()
+ }
+
+ @Test
+ fun collectFalsingSignals_screenOnAndOff_aodAvailable() =
+ testScope.runTest {
+ keyguardRepository.setAodAvailable(true)
+ runCurrent()
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+ }
+
+ @Test
+ fun collectFalsingSignals_bouncerVisibility() =
+ testScope.runTest {
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector).onBouncerHidden()
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
+ runCurrent()
+ verify(falsingCollector).onBouncerShown()
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+ runCurrent()
+ verify(falsingCollector, times(2)).onBouncerHidden()
+ }
+
private fun prepareState(
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
@@ -334,11 +545,23 @@ class SceneContainerStartableTest : SysuiTestCase() {
lastWakeReason = WakeSleepReason.POWER_BUTTON,
lastSleepReason = WakeSleepReason.POWER_BUTTON
)
- private val STARTING_TO_WAKE =
+ private val ASLEEP =
+ WakefulnessModel(
+ state = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON
+ )
+ private val STARTING_TO_WAKE_FROM_POWER_BUTTON =
WakefulnessModel(
state = WakefulnessState.STARTING_TO_WAKE,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
lastSleepReason = WakeSleepReason.POWER_BUTTON
)
+ private val STARTING_TO_WAKE_FROM_TAP =
+ WakefulnessModel(
+ state = WakefulnessState.STARTING_TO_WAKE,
+ lastWakeReason = WakeSleepReason.TAP,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 88abb642f7c1..0b56a59ca467 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -39,7 +39,8 @@ class SceneContainerViewModelTest : SysuiTestCase() {
private val interactor = utils.sceneInteractor()
private val underTest =
SceneContainerViewModel(
- interactor = interactor,
+ sceneInteractor = interactor,
+ falsingInteractor = utils.falsingInteractor(),
)
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 209dcc1dd203..c573ac638032 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -716,7 +716,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mAmbientState,
mRecordingController,
mFalsingManager,
- new FalsingCollectorFake(),
mAccessibilityManager,
mLockscreenGestureLogger,
mMetricsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 1738b06cc0b3..dfd782b73a22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -63,6 +63,8 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.google.common.util.concurrent.MoreExecutors;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,6 +75,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.util.List;
+import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -98,6 +101,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
+ private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private float mPreferredRefreshRate = -1;
@@ -125,6 +129,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mConfigurationController,
mKeyguardViewMediator,
mKeyguardBypassController,
+ mBackgroundExecutor,
mColorExtractor,
mDumpManager,
mKeyguardStateController,
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 3cce4232ab7a..2aea1f934fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -56,6 +56,8 @@ import com.android.systemui.statusbar.notification.data.repository.NotificationE
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.DozeScrimController
+import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
@@ -90,6 +92,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock private lateinit var view: NotificationShadeWindowView
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var dozeServiceHost: DozeServiceHost
+ @Mock private lateinit var dozeScrimController: DozeScrimController
@Mock private lateinit var backActionInteractor: BackActionInteractor
@Mock private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@@ -168,6 +172,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
statusBarWindowStateController,
lockIconViewController,
centralSurfaces,
+ dozeServiceHost,
+ dozeScrimController,
backActionInteractor,
powerInteractor,
notificationShadeWindowController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 66d48d64b3a3..1ab613485c9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -56,6 +56,8 @@ import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.DozeScrimController
+import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -89,6 +91,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var shadeController: ShadeController
@Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var dozeServiceHost: DozeServiceHost
+ @Mock private lateinit var dozeScrimController: DozeScrimController
@Mock private lateinit var backActionInteractor: BackActionInteractor
@Mock private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@@ -174,6 +178,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
statusBarWindowStateController,
lockIconViewController,
centralSurfaces,
+ dozeServiceHost,
+ dozeScrimController,
backActionInteractor,
powerInteractor,
notificationShadeWindowController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index e22e5713d699..ab0ae05b0e83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -37,7 +37,6 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.TestScopeProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
@@ -132,7 +131,6 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@Mock protected AmbientState mAmbientState;
@Mock protected RecordingController mRecordingController;
@Mock protected FalsingManager mFalsingManager;
- @Mock protected FalsingCollector mFalsingCollector;
@Mock protected AccessibilityManager mAccessibilityManager;
@Mock protected LockscreenGestureLogger mLockscreenGestureLogger;
@Mock protected MetricsLogger mMetricsLogger;
@@ -242,7 +240,6 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
mAmbientState,
mRecordingController,
mFalsingManager,
- mFalsingCollector,
mAccessibilityManager,
mLockscreenGestureLogger,
mMetricsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 0cc0b987aa34..7c8199eaa0e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -27,7 +27,6 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.FalsingManager
@@ -57,6 +56,7 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.SystemClock
import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -68,7 +68,6 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
-import java.util.Optional
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -100,7 +99,6 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
private val gutsManager: NotificationGutsManager = mock()
private val onUserInteractionCallback: OnUserInteractionCallback = mock()
private val falsingManager: FalsingManager = mock()
- private val falsingCollector: FalsingCollector = mock()
private val featureFlags: FeatureFlags = mock()
private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
private val bubblesManager: BubblesManager = mock()
@@ -140,7 +138,6 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
/*allowLongPress=*/ false,
onUserInteractionCallback,
falsingManager,
- falsingCollector,
featureFlags,
peopleNotificationIdentifier,
Optional.of(bubblesManager),
@@ -226,20 +223,20 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
fun registerSettingsListener_forBubbles() {
controller.init(mock(NotificationEntry::class.java))
val viewStateObserver = withArgCaptor {
- verify(view).addOnAttachStateChangeListener(capture());
+ verify(view).addOnAttachStateChangeListener(capture())
}
- viewStateObserver.onViewAttachedToWindow(view);
- verify(settingsController).addCallback(any(), any());
+ viewStateObserver.onViewAttachedToWindow(view)
+ verify(settingsController).addCallback(any(), any())
}
@Test
fun unregisterSettingsListener_forBubbles() {
controller.init(mock(NotificationEntry::class.java))
val viewStateObserver = withArgCaptor {
- verify(view).addOnAttachStateChangeListener(capture());
+ verify(view).addOnAttachStateChangeListener(capture())
}
- viewStateObserver.onViewDetachedFromWindow(view);
- verify(settingsController).removeCallback(any(), any());
+ viewStateObserver.onViewDetachedFromWindow(view)
+ verify(settingsController).removeCallback(any(), any())
}
@Test
@@ -263,11 +260,17 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
whenever(view.privateLayout).thenReturn(childView)
controller.mSettingsListener.onSettingChanged(
- BUBBLES_SETTING_URI, view.entry.sbn.userId, "1")
+ BUBBLES_SETTING_URI,
+ view.entry.sbn.userId,
+ "1"
+ )
verify(childView).setBubblesEnabledForUser(true)
controller.mSettingsListener.onSettingChanged(
- BUBBLES_SETTING_URI, view.entry.sbn.userId, "9")
+ BUBBLES_SETTING_URI,
+ view.entry.sbn.userId,
+ "9"
+ )
verify(childView).setBubblesEnabledForUser(false)
}
@@ -277,13 +280,12 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
whenever(view.privateLayout).thenReturn(childView)
val notification = Notification.Builder(mContext).build()
- val sbn = SbnBuilder().setNotification(notification)
- .setUser(UserHandle.of(USER_ALL))
- .build()
- whenever(view.entry).thenReturn(NotificationEntryBuilder()
- .setSbn(sbn)
- .setUser(UserHandle.of(USER_ALL))
- .build())
+ val sbn =
+ SbnBuilder().setNotification(notification).setUser(UserHandle.of(USER_ALL)).build()
+ whenever(view.entry)
+ .thenReturn(
+ NotificationEntryBuilder().setSbn(sbn).setUser(UserHandle.of(USER_ALL)).build()
+ )
controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, 9, "1")
verify(childView).setBubblesEnabledForUser(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 0425830db8ba..9dfcb3f75eb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -54,7 +54,6 @@ import androidx.annotation.NonNull;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.TestableDependency;
-import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
@@ -600,7 +599,6 @@ public class NotificationTestHelper {
mock(OnExpandClickListener.class),
mock(ExpandableNotificationRow.CoordinateOnClickListener.class),
new FalsingManagerFake(),
- new FalsingCollectorFake(),
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 7ebf6c81d2d2..ac9cfb3cfb23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -341,6 +341,7 @@ public class BubblesTest extends SysuiTestCase {
mConfigurationController,
mKeyguardViewMediator,
mKeyguardBypassController,
+ syncExecutor,
mColorExtractor,
mDumpManager,
mKeyguardStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 4f33a97139b2..f7db44e9618a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -27,6 +27,9 @@ import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -98,6 +101,9 @@ class SceneTestUtils(
private val context = test.context
+ private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() }
+ private var falsingInteractor: FalsingInteractor? = null
+
fun fakeSceneContainerRepository(
containerConfig: SceneContainerConfig = fakeSceneContainerConfig(),
): SceneContainerRepository {
@@ -175,6 +181,7 @@ class SceneTestUtils(
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
featureFlags = featureFlags,
+ falsingInteractor = falsingInteractor(),
)
}
@@ -191,6 +198,14 @@ class SceneTestUtils(
)
}
+ fun falsingInteractor(collector: FalsingCollector = falsingCollector()): FalsingInteractor {
+ return falsingInteractor ?: FalsingInteractor(collector).also { falsingInteractor = it }
+ }
+
+ fun falsingCollector(): FalsingCollector {
+ return falsingCollectorFake
+ }
+
private fun applicationScope(): CoroutineScope {
return testScope.backgroundScope
}
diff --git a/packages/services/VirtualCamera/OWNERS b/packages/services/VirtualCamera/OWNERS
new file mode 100644
index 000000000000..c66443fb8a14
--- /dev/null
+++ b/packages/services/VirtualCamera/OWNERS
@@ -0,0 +1,3 @@
+include /services/companion/java/com/android/server/companion/virtual/OWNERS
+caen@google.com
+jsebechlebsky@google.com \ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index f3a540b1c7a5..cd83f8f4d9d8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -389,11 +389,19 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
@Override
public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent,
int policyFlags) {
+ if (!mInstalled) {
+ Slog.w(TAG, "onMotionEvent called before input filter installed!");
+ return;
+ }
sendInputEvent(transformedEvent, policyFlags);
}
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (!mInstalled) {
+ Slog.w(TAG, "onKeyEvent called before input filter installed!");
+ return;
+ }
sendInputEvent(event, policyFlags);
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 4688658bf1c3..423b85f9305f 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -56,7 +56,7 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
- private final FillServiceCallbacks mCallbacks;
+ private FillServiceCallbacks mCallbacks;
private final Object mLock = new Object();
private CompletableFuture<FillResponse> mPendingFillRequest;
private int mPendingFillRequestId = INVALID_REQUEST_ID;
@@ -128,9 +128,12 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
*/
public int cancelCurrentRequest() {
synchronized (mLock) {
- return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+ int canceledRequestId = mPendingFillRequest != null && mPendingFillRequest.cancel(false)
? mPendingFillRequestId
: INVALID_REQUEST_ID;
+ mPendingFillRequest = null;
+ mPendingFillRequestId = INVALID_REQUEST_ID;
+ return canceledRequestId;
}
}
@@ -184,6 +187,10 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
mPendingFillRequest = null;
mPendingFillRequestId = INVALID_REQUEST_ID;
}
+ if (mCallbacks == null) {
+ Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
+ return;
+ }
if (err == null) {
mCallbacks.onFillRequestSuccess(request.getId(), res,
mComponentName.getPackageName(), request.getFlags());
@@ -220,6 +227,10 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
return save;
}).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS)
.whenComplete((res, err) -> Handler.getMain().post(() -> {
+ if (mCallbacks == null) {
+ Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
+ return;
+ }
if (err == null) {
mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), res);
} else {
@@ -234,6 +245,8 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
}
public void destroy() {
+ cancelCurrentRequest();
unbind();
+ mCallbacks = null;
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4d249abafdf1..a78764456d8b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -32,6 +32,7 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.IInstalld.IFsveritySetupAuthToken;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
@@ -678,6 +679,7 @@ class StorageManagerService extends IStorageManager.Stub
private static final int H_VOLUME_STATE_CHANGED = 15;
private static final int H_CLOUD_MEDIA_PROVIDER_CHANGED = 16;
private static final int H_SECURE_KEYGUARD_STATE_CHANGED = 17;
+ private static final int H_REMOUNT_VOLUMES_ON_MOVE = 18;
class StorageManagerServiceHandler extends Handler {
public StorageManagerServiceHandler(Looper looper) {
@@ -833,6 +835,10 @@ class StorageManagerService extends IStorageManager.Stub
}
break;
}
+ case H_REMOUNT_VOLUMES_ON_MOVE: {
+ remountVolumesForRunningUsersOnMove();
+ break;
+ }
}
}
}
@@ -1286,6 +1292,44 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ /**
+ * This method informs vold and storaged that the user has stopped and started whenever move
+ * storage is performed. This ensures that the correct emulated volumes are mounted for the
+ * users other than the current user. This solves an edge case wherein the correct emulated
+ * volumes are not mounted, this will cause the media data to be still stored on internal
+ * storage whereas the data should be stored in the adopted primary storage. This method stops
+ * the users at vold first which will remove the old volumes which and starts the users at vold
+ * which will reattach the correct volumes. This does not performs a full reset as full reset
+ * clears every state from vold and SMS {@link #resetIfRebootedAndConnected} which is expensive
+ * and causes instability.
+ */
+ private void remountVolumesForRunningUsersOnMove() {
+ // Do not want to hold the lock for long
+ final List<Integer> unlockedUsers = new ArrayList<>();
+ synchronized (mLock) {
+ for (int userId : mSystemUnlockedUsers) {
+ if (userId == mCurrentUserId) continue;
+ unlockedUsers.add(userId);
+ }
+ }
+ for (Integer userId : unlockedUsers) {
+ try {
+ mVold.onUserStopped(userId);
+ mStoraged.onUserStopped(userId);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+ for (Integer userId : unlockedUsers) {
+ try {
+ mVold.onUserStarted(userId);
+ mStoraged.onUserStarted(userId);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+ }
+
private boolean supportsBlockCheckpoint() throws RemoteException {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
return mVold.supportsBlockCheckpoint();
@@ -1820,6 +1864,7 @@ class StorageManagerService extends IStorageManager.Stub
mPrimaryStorageUuid = mMoveTargetUuid;
writeSettingsLocked();
+ mHandler.obtainMessage(H_REMOUNT_VOLUMES_ON_MOVE).sendToTarget();
}
if (PackageManager.isMoveStatusFinished(status)) {
@@ -4640,7 +4685,7 @@ class StorageManagerService extends IStorageManager.Stub
pw.print(") total size: ");
pw.print(pair.second);
pw.print(" (");
- pw.print(DataUnit.MEBIBYTES.toBytes(pair.second));
+ pw.print(pair.second / DataUnit.MEBIBYTES.toBytes(1L));
pw.println(" MiB)");
}
@@ -4950,5 +4995,24 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ @Override
+ public IFsveritySetupAuthToken createFsveritySetupAuthToken(ParcelFileDescriptor authFd,
+ int appUid, @UserIdInt int userId) throws IOException {
+ try {
+ return mInstaller.createFsveritySetupAuthToken(authFd, appUid, userId);
+ } catch (Installer.InstallerException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public int enableFsverity(IFsveritySetupAuthToken authToken, String filePath,
+ String packageName) throws IOException {
+ try {
+ return mInstaller.enableFsverity(authToken, filePath, packageName);
+ } catch (Installer.InstallerException e) {
+ throw new IOException(e);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 68062b566906..65f6af7d0309 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsAppOpsTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -31,7 +31,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.BackgroundPermissionsTest"
diff --git a/services/core/java/com/android/server/audio/UuidUtils.java b/services/core/java/com/android/server/audio/UuidUtils.java
index 2003619b583c..035bea32da4f 100644
--- a/services/core/java/com/android/server/audio/UuidUtils.java
+++ b/services/core/java/com/android/server/audio/UuidUtils.java
@@ -45,26 +45,24 @@ class UuidUtils {
* Generate a headtracking UUID from AudioDeviceAttributes
*/
public static UUID uuidFromAudioDeviceAttributes(AudioDeviceAttributes device) {
- switch (device.getInternalType()) {
- case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP:
- String address = device.getAddress().replace(":", "");
- if (address.length() != 12) {
- return null;
- }
- address = "0x" + address;
- long lsb = LSB_PREFIX_BT;
- try {
- lsb |= Long.decode(address).longValue();
- } catch (NumberFormatException e) {
- return null;
- }
- if (AudioService.DEBUG_DEVICES) {
- Slog.i(TAG, "uuidFromAudioDeviceAttributes lsb: " + Long.toHexString(lsb));
- }
- return new UUID(0, lsb);
- default:
- // Handle other device types here
- return null;
+ if (!AudioSystem.isBluetoothA2dpOutDevice(device.getInternalType())
+ && !AudioSystem.isBluetoothLeOutDevice(device.getInternalType())) {
+ return null;
}
+ String address = device.getAddress().replace(":", "");
+ if (address.length() != 12) {
+ return null;
+ }
+ address = "0x" + address;
+ long lsb = LSB_PREFIX_BT;
+ try {
+ lsb |= Long.decode(address).longValue();
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ if (AudioService.DEBUG_DEVICES) {
+ Slog.i(TAG, "uuidFromAudioDeviceAttributes lsb: " + Long.toHexString(lsb));
+ }
+ return new UUID(0, lsb);
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStats.java b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
index e109cc8011e7..707240bf41f8 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStats.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
@@ -90,6 +90,11 @@ public class AuthenticationStats {
mRejectedAttempts = 0;
}
+ /** Update enrollment notification counter after sending a notification. */
+ public void updateNotificationCounter() {
+ mEnrollmentNotifications++;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 3d1b162d508b..e8a20def02cb 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -68,8 +68,10 @@ public class AuthenticationStatsCollector {
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
if (userId != UserHandle.USER_NULL
&& intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
+ Slog.d(TAG, "Removing data for user: " + userId);
onUserRemoved(userId);
}
}
@@ -84,7 +86,9 @@ public class AuthenticationStatsCollector {
mModality = modality;
mBiometricNotification = biometricNotification;
- context.registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ context.registerReceiver(mBroadcastReceiver, intentFilter);
}
private void initializeUserAuthenticationStatsMap() {
@@ -121,10 +125,11 @@ public class AuthenticationStatsCollector {
authenticationStats.authenticate(authenticated);
+ sendNotificationIfNeeded(userId);
+
if (mPersisterInitialized) {
persistDataIfNeeded(userId);
}
- sendNotificationIfNeeded(userId);
}
/** Check if a notification should be sent after a calculation cycle. */
@@ -164,8 +169,10 @@ public class AuthenticationStatsCollector {
}
if (hasEnrolledFace && !hasEnrolledFingerprint) {
mBiometricNotification.sendFpEnrollNotification(mContext);
+ authenticationStats.updateNotificationCounter();
} else if (!hasEnrolledFace && hasEnrolledFingerprint) {
mBiometricNotification.sendFaceEnrollNotification(mContext);
+ authenticationStats.updateNotificationCounter();
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
index 74e1410dba00..8122b1d131f8 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
@@ -59,7 +59,7 @@ public class AuthenticationStatsPersister {
// The package info in the context isn't initialized in the way it is for normal apps,
// so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
// build the path manually below using the same policy that appears in ContextImpl.
- final File prefsFile = new File(Environment.getDataSystemDeDirectory(), FILE_NAME);
+ final File prefsFile = new File(Environment.getDataSystemDirectory(), FILE_NAME);
mSharedPreferences = context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
}
@@ -137,16 +137,19 @@ public class AuthenticationStatsPersister {
iterator.remove();
break;
}
+ // Reset frrStatJson when user doesn't exist.
+ frrStatJson = null;
}
- // If there's existing frr stats in the file, we want to update the stats for the given
- // modality and keep the stats for other modalities.
+ // Checks if this is a new user and there's no JSON for this user in the storage.
if (frrStatJson == null) {
frrStatJson = new JSONObject().put(USER_ID, userId);
}
frrStatsSet.add(buildFrrStats(frrStatJson, totalAttempts, rejectedAttempts,
enrollmentNotifications, modality));
+ Slog.d(TAG, "frrStatsSet to persist: " + frrStatsSet);
+
mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply();
} catch (JSONException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index 2ff695d7b85d..0fc8ababbafb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -95,8 +95,6 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FACE_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -120,8 +118,6 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index cba5039f714d..ff35b192cccd 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -3296,13 +3296,6 @@ public class Vpn {
}
agentConnect(this::onValidationStatus);
return; // Link properties are already sent.
- } else {
- // Underlying networks also set in agentConnect()
- doSetUnderlyingNetworks(networkAgent, Collections.singletonList(network));
- mNetworkCapabilities =
- new NetworkCapabilities.Builder(mNetworkCapabilities)
- .setUnderlyingNetworks(Collections.singletonList(network))
- .build();
}
lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked
@@ -3384,8 +3377,6 @@ public class Vpn {
final LinkProperties oldLp = makeLinkProperties();
- final boolean underlyingNetworkHasChanged =
- !Arrays.equals(mConfig.underlyingNetworks, new Network[]{network});
mConfig.underlyingNetworks = new Network[] {network};
mConfig.mtu = calculateVpnMtu();
@@ -3417,18 +3408,9 @@ public class Vpn {
removed.getAddress(), removed.getPrefixLength());
}
} else {
- // Put below 3 updates into else block is because agentConnect() will do
- // those things, so there is no need to do the redundant work.
+ // Put below update into else block is because agentConnect() will do
+ // the same things, so there is no need to do the redundant work.
if (!newLp.equals(oldLp)) doSendLinkProperties(mNetworkAgent, newLp);
- if (underlyingNetworkHasChanged) {
- mNetworkCapabilities =
- new NetworkCapabilities.Builder(mNetworkCapabilities)
- .setUnderlyingNetworks(
- Collections.singletonList(network))
- .build();
- doSetUnderlyingNetworks(mNetworkAgent,
- Collections.singletonList(network));
- }
}
}
@@ -3554,10 +3536,28 @@ public class Vpn {
*/
private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
if (underlyingNetwork == null) {
+ // For null underlyingNetwork case, there will not be a NetworkAgent available so
+ // no underlying network update is necessary here. Note that updating
+ // mNetworkCapabilities here would also be reasonable, but it will be updated next
+ // time the VPN connects anyway.
Log.d(TAG, "There is no active network for starting an IKE session");
return;
}
+ final List<Network> networks = Collections.singletonList(underlyingNetwork);
+ // Update network capabilities if underlying network is changed.
+ if (!networks.equals(mNetworkCapabilities.getUnderlyingNetworks())) {
+ mNetworkCapabilities =
+ new NetworkCapabilities.Builder(mNetworkCapabilities)
+ .setUnderlyingNetworks(networks)
+ .build();
+ // No NetworkAgent case happens when Vpn tries to start a new VPN. The underlying
+ // network update will be done later with NetworkAgent connected event.
+ if (mNetworkAgent != null) {
+ doSetUnderlyingNetworks(mNetworkAgent, networks);
+ }
+ }
+
if (maybeMigrateIkeSessionAndUpdateVpnTransportInfo(underlyingNetwork)) return;
startIkeSession(underlyingNetwork);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index be6133b5b6ae..1afa3ed97463 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -118,7 +118,6 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -533,6 +532,8 @@ public class NotificationManagerService extends SystemService {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
private static final long NOTIFICATION_LOG_ASSISTANT_CANCEL = 195579280L;
+ private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30);
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -6676,22 +6677,14 @@ public class NotificationManagerService extends SystemService {
}
private PostNotificationTracker acquireWakeLockForPost(String pkg, int uid) {
- if (mFlagResolver.isEnabled(WAKE_LOCK_FOR_POSTING_NOTIFICATION)
- && Binder.withCleanCallingIdentity(
- () -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, false))) {
- // The package probably doesn't have WAKE_LOCK permission and should not require it.
- return Binder.withCleanCallingIdentity(() -> {
- WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "NotificationManagerService:post:" + pkg);
- wakeLock.setWorkSource(new WorkSource(uid, pkg));
- // TODO(b/275044361): Adjust to a more reasonable number when we have the data.
- wakeLock.acquire(30_000);
- return mPostNotificationTrackerFactory.newTracker(wakeLock);
- });
- } else {
- return mPostNotificationTrackerFactory.newTracker(null);
- }
+ // The package probably doesn't have WAKE_LOCK permission and should not require it.
+ return Binder.withCleanCallingIdentity(() -> {
+ WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "NotificationManagerService:post:" + pkg);
+ wakeLock.setWorkSource(new WorkSource(uid, pkg));
+ wakeLock.acquire(POST_WAKE_LOCK_TIMEOUT.toMillis());
+ return mPostNotificationTrackerFactory.newTracker(wakeLock);
+ });
}
/**
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 66a170397b84..a988821fe915 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -23,9 +23,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DELETE_SUCCEEDED;
import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
@@ -72,14 +69,13 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.VMRuntime;
-import java.util.Collections;
import java.util.List;
/**
* Deletes a package. Uninstall if installed, or at least deletes the base directory if it's called
* from a failed installation. Fixes user state after deletion.
* Handles special treatments to system apps.
- * Relies on RemovePackageHelper to clear internal data structures.
+ * Relies on RemovePackageHelper to clear internal data structures and remove app data.
*/
final class DeletePackageHelper {
private static final boolean DEBUG_CLEAN_APKS = false;
@@ -90,24 +86,17 @@ final class DeletePackageHelper {
private final UserManagerInternal mUserManagerInternal;
private final PermissionManagerServiceInternal mPermissionManager;
private final RemovePackageHelper mRemovePackageHelper;
- private final AppDataHelper mAppDataHelper;
// TODO(b/198166813): remove PMS dependency
- DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
- AppDataHelper appDataHelper) {
+ DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper) {
mPm = pm;
mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mRemovePackageHelper = removePackageHelper;
- mAppDataHelper = appDataHelper;
}
DeletePackageHelper(PackageManagerService pm) {
- mPm = pm;
- mAppDataHelper = new AppDataHelper(mPm);
- mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
- mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
- mRemovePackageHelper = new RemovePackageHelper(mPm, mAppDataHelper);
+ this(pm, new RemovePackageHelper(pm));
}
/**
@@ -484,7 +473,7 @@ final class DeletePackageHelper {
}
}
if (clearPackageStateAndReturn) {
- clearPackageStateForUserLIF(ps, userId, outInfo, flags);
+ mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, outInfo, flags);
mPm.scheduleWritePackageRestrictions(user);
return;
}
@@ -531,55 +520,6 @@ final class DeletePackageHelper {
}
}
- private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
- PackageRemovedInfo outInfo, int flags) {
- final AndroidPackage pkg;
- final SharedUserSetting sus;
- synchronized (mPm.mLock) {
- pkg = mPm.mPackages.get(ps.getPackageName());
- sus = mPm.mSettings.getSharedUserSettingLPr(ps);
- }
-
- mAppDataHelper.destroyAppProfilesLIF(pkg);
-
- final List<AndroidPackage> sharedUserPkgs =
- sus != null ? sus.getPackages() : Collections.emptyList();
- final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
- final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
- : new int[] {userId};
- for (int nextUserId : userIds) {
- if (DEBUG_REMOVE) {
- Slog.d(TAG, "Updating package:" + ps.getPackageName() + " install state for user:"
- + nextUserId);
- }
- if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
- mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
- FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
- ps.setCeDataInode(-1, nextUserId);
- }
- mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
- preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
- nextUserId);
- mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
- }
- mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg,
- sharedUserPkgs, userId);
-
- if (outInfo != null) {
- if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
- outInfo.mDataRemoved = true;
- }
- outInfo.mRemovedPackage = ps.getPackageName();
- outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName;
- outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null;
- outInfo.mRemovedAppId = ps.getAppId();
- outInfo.mRemovedUsers = userIds;
- outInfo.mBroadcastUsers = userIds;
- outInfo.mIsExternal = ps.isExternalStorage();
- outInfo.mRemovedPackageVersionCode = ps.getVersionCode();
- }
- }
-
@GuardedBy("mPm.mInstallLock")
private void deleteInstalledPackageLIF(PackageSetting ps,
boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9ac983dfebeb..6233c9bc4dad 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -29,6 +29,7 @@ import android.os.CreateAppDataArgs;
import android.os.CreateAppDataResult;
import android.os.IBinder;
import android.os.IInstalld;
+import android.os.ParcelFileDescriptor;
import android.os.ReconcileSdkDataArgs;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -1161,6 +1162,55 @@ public class Installer extends SystemService {
}
}
+ /**
+ * Returns an auth token for the provided writable FD.
+ *
+ * @param authFd a file descriptor to proof that the caller can write to the file.
+ * @param appUid uid of the calling app.
+ * @param userId id of the user whose app file to enable fs-verity.
+ *
+ * @return authToken, or null if a remote call shouldn't be continued. See {@link
+ * #checkBeforeRemote}.
+ *
+ * @throws InstallerException if the remote call failed.
+ */
+ public IInstalld.IFsveritySetupAuthToken createFsveritySetupAuthToken(
+ ParcelFileDescriptor authFd, int appUid, @UserIdInt int userId)
+ throws InstallerException {
+ if (!checkBeforeRemote()) {
+ return null;
+ }
+ try {
+ return mInstalld.createFsveritySetupAuthToken(authFd, appUid, userId);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ /**
+ * Enables fs-verity to the given app file.
+ *
+ * @param authToken a token previously returned from {@link #createFsveritySetupAuthToken}.
+ * @param filePath file path of the package to enable fs-verity.
+ * @param packageName name of the package.
+ *
+ * @return 0 if the operation was successful, otherwise {@code errno}.
+ *
+ * @throws InstallerException if the remote call failed (e.g. see {@link #checkBeforeRemote}).
+ */
+ public int enableFsverity(IInstalld.IFsveritySetupAuthToken authToken, String filePath,
+ String packageName) throws InstallerException {
+ if (!checkBeforeRemote()) {
+ throw new InstallerException("fs-verity wasn't enabled with an isolated installer");
+ }
+ BlockGuard.getVmPolicy().onPathAccess(filePath);
+ try {
+ return mInstalld.enableFsverity(authToken, filePath, packageName);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
public static class InstallerException extends Exception {
public InstallerException(String detailMessage) {
super(detailMessage);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 823c9af27de6..ac7842948639 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1971,9 +1971,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mApexManager = injector.getApexManager();
mAppsFilter = mInjector.getAppsFilter();
- mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
- mInjector.getUserManagerInternal(), new DeletePackageHelper(this));
-
mChangedPackagesTracker = new ChangedPackagesTracker();
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
@@ -1987,8 +1984,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
- mAppDataHelper);
+ mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper);
+
+ mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
+ mInjector.getUserManagerInternal(), mDeletePackageHelper);
+
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
mPreferredActivityHelper = new PreferredActivityHelper(this);
mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 6d3b26cc2fd4..d4f30fed6967 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -253,6 +253,56 @@ final class RemovePackageHelper {
}
}
+ public void clearPackageStateForUserLIF(PackageSetting ps, int userId,
+ PackageRemovedInfo outInfo, int flags) {
+ final AndroidPackage pkg;
+ final SharedUserSetting sus;
+ synchronized (mPm.mLock) {
+ pkg = mPm.mPackages.get(ps.getPackageName());
+ sus = mPm.mSettings.getSharedUserSettingLPr(ps);
+ }
+
+ mAppDataHelper.destroyAppProfilesLIF(pkg);
+
+ final List<AndroidPackage> sharedUserPkgs =
+ sus != null ? sus.getPackages() : Collections.emptyList();
+ final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
+ final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
+ : new int[] {userId};
+ for (int nextUserId : userIds) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Updating package:" + ps.getPackageName() + " install state for user:"
+ + nextUserId);
+ }
+ if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+ mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
+ ps.setCeDataInode(-1, nextUserId);
+ }
+ mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
+ preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
+ nextUserId);
+ mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
+ }
+ mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg,
+ sharedUserPkgs, userId);
+
+ if (outInfo != null) {
+ if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+ outInfo.mDataRemoved = true;
+ }
+ outInfo.mRemovedPackage = ps.getPackageName();
+ outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName;
+ outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null;
+ outInfo.mRemovedAppId = ps.getAppId();
+ outInfo.mRemovedUsers = userIds;
+ outInfo.mBroadcastUsers = userIds;
+ outInfo.mIsExternal = ps.isExternalStorage();
+ outInfo.mRemovedPackageVersionCode = ps.getVersionCode();
+ }
+ }
+
+ // Called to clean up disabled system packages
public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
synchronized (mPm.mInstallLock) {
@@ -314,7 +364,6 @@ final class RemovePackageHelper {
int removedAppId = -1;
// writer
- boolean installedStateChanged = false;
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mPm.mLock) {
@@ -354,9 +403,10 @@ final class RemovePackageHelper {
mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
}
}
- // make sure to preserve per-user disabled state if this removal was just
+ // make sure to preserve per-user installed state if this removal was just
// a downgrade of a system app to the factory package
- if (outInfo != null && outInfo.mOrigUsers != null) {
+ boolean installedStateChanged = false;
+ if (outInfo != null && outInfo.mOrigUsers != null && deletedPs.isSystem()) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index dd434fbeecb4..3e4dd1637387 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3611,8 +3611,8 @@ public class ShortcutService extends IShortcutService.Stub {
// Otherwise check persisted shortcuts
getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
- cb.complete(getShortcutIconUriInternal(launcherUserId, launcherPackage,
- packageName, si, userId));
+ cb.complete(si == null ? null : getShortcutIconUriInternal(launcherUserId,
+ launcherPackage, packageName, si, userId));
});
}
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index b2dcf379fe7d..24323c8bfbde 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.BackgroundPermissionsTest"
@@ -32,7 +32,7 @@
"name": "CtsPermissionPolicyTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
diff --git a/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java b/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
index 65325c297719..7c4d7875b76f 100644
--- a/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
+++ b/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
@@ -22,6 +22,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import java.util.NoSuchElementException;
+
public class BootControlHIDL implements IBootControl {
private static final String TAG = "BootControlHIDL";
@@ -32,7 +34,7 @@ public class BootControlHIDL implements IBootControl {
public static boolean isServicePresent() {
try {
android.hardware.boot.V1_0.IBootControl.getService(true);
- } catch (RemoteException e) {
+ } catch (RemoteException | NoSuchElementException e) {
return false;
}
return true;
@@ -41,7 +43,7 @@ public class BootControlHIDL implements IBootControl {
public static boolean isV1_2ServicePresent() {
try {
android.hardware.boot.V1_2.IBootControl.getService(true);
- } catch (RemoteException e) {
+ } catch (RemoteException | NoSuchElementException e) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 95296210be80..3aed6e30cf87 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -27,10 +27,12 @@ import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
import android.security.IFileIntegrityService;
import android.util.Slog;
@@ -54,6 +56,7 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Objects;
/**
* A {@link SystemService} that provides file integrity related operations.
@@ -112,7 +115,7 @@ public class FileIntegrityService extends SystemService {
.exec(this, in, out, err, args, callback, resultReceiver);
}
- private void checkCallerPermission(String packageName) {
+ private void checkCallerPackageName(String packageName) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageManagerInternal packageManager =
@@ -123,7 +126,10 @@ public class FileIntegrityService extends SystemService {
throw new SecurityException(
"Calling uid " + callingUid + " does not own package " + packageName);
}
+ }
+ private void checkCallerPermission(String packageName) {
+ checkCallerPackageName(packageName);
if (getContext().checkCallingPermission(android.Manifest.permission.INSTALL_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
return;
@@ -131,12 +137,43 @@ public class FileIntegrityService extends SystemService {
final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
final int mode = appOpsManager.checkOpNoThrow(
- AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, callingUid, packageName);
+ AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, Binder.getCallingUid(), packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
throw new SecurityException(
"Caller should have INSTALL_PACKAGES or REQUEST_INSTALL_PACKAGES");
}
}
+
+ @Override
+ public android.os.IInstalld.IFsveritySetupAuthToken createAuthToken(
+ ParcelFileDescriptor authFd) throws RemoteException {
+ Objects.requireNonNull(authFd);
+ try {
+ var authToken = getStorageManagerInternal().createFsveritySetupAuthToken(authFd,
+ Binder.getCallingUid(), Binder.getCallingUserHandle().getIdentifier());
+ // fs-verity setup requires no writable fd to the file. Release the dup now that
+ // it's passed.
+ authFd.close();
+ return authToken;
+ } catch (IOException e) {
+ throw new RemoteException(e);
+ }
+ }
+
+ @Override
+ public int setupFsverity(android.os.IInstalld.IFsveritySetupAuthToken authToken,
+ String filePath, String packageName) throws RemoteException {
+ Objects.requireNonNull(authToken);
+ Objects.requireNonNull(filePath);
+ Objects.requireNonNull(packageName);
+ checkCallerPackageName(packageName);
+
+ try {
+ return getStorageManagerInternal().enableFsverity(authToken, filePath, packageName);
+ } catch (IOException e) {
+ throw new RemoteException(e);
+ }
+ }
};
public FileIntegrityService(final Context context) {
@@ -146,9 +183,19 @@ public class FileIntegrityService extends SystemService {
} catch (CertificateException e) {
Slog.wtf(TAG, "Cannot get an instance of X.509 certificate factory");
}
+
LocalServices.addService(FileIntegrityService.class, this);
}
+ /**
+ * Returns StorageManagerInternal as a proxy to fs-verity related calls. This is to plumb
+ * the call through the canonical Installer instance in StorageManagerService, since the
+ * Installer instance isn't directly accessible.
+ */
+ private StorageManagerInternal getStorageManagerInternal() {
+ return LocalServices.getService(StorageManagerInternal.class);
+ }
+
@Override
public void onStart() {
loadAllCertificates();
diff --git a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
index f744d00b2066..565eb6e85559 100644
--- a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
+++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
@@ -18,7 +18,6 @@ package com.android.server.sensorprivacy;
import static android.hardware.SensorManager.SENSOR_DELAY_NORMAL;
-import android.annotation.ColorInt;
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.Sensor;
@@ -39,6 +38,7 @@ import android.util.Pair;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.server.FgThread;
import java.util.ArrayDeque;
@@ -48,12 +48,10 @@ import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-
class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener,
SensorEventListener {
- @VisibleForTesting
- static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1);
+ private static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1);
private final Handler mHandler;
private final Executor mExecutor;
@@ -69,11 +67,6 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
private LightsManager.LightsSession mLightsSession = null;
- @ColorInt
- private final int mDayColor;
- @ColorInt
- private final int mNightColor;
-
private final Sensor mLightSensor;
private boolean mIsAmbientLightListenerRegistered = false;
@@ -81,7 +74,9 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
/** When average of the time integral over the past {@link #mMovingAverageIntervalMillis}
* milliseconds of the log_1.1(lux(t)) is greater than this value, use the daytime brightness
* else use nighttime brightness. */
- private final long mNightThreshold;
+ private final long[] mThresholds;
+
+ private final int[] mColors;
private final ArrayDeque<Pair<Long, Integer>> mAmbientLightValues = new ArrayDeque<>();
/** Tracks the Riemann sum of {@link #mAmbientLightValues} to avoid O(n) operations when sum is
* needed */
@@ -101,6 +96,20 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
@VisibleForTesting
CameraPrivacyLightController(Context context, Looper looper) {
+ mColors = context.getResources().getIntArray(R.array.config_cameraPrivacyLightColors);
+ if (ArrayUtils.isEmpty(mColors)) {
+ mHandler = null;
+ mExecutor = null;
+ mContext = null;
+ mAppOpsManager = null;
+ mLightsManager = null;
+ mSensorManager = null;
+ mLightSensor = null;
+ mMovingAverageIntervalMillis = 0;
+ mThresholds = null;
+ // Return here before this class starts interacting with other services.
+ return;
+ }
mContext = context;
mHandler = new Handler(looper);
@@ -109,14 +118,20 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mLightsManager = mContext.getSystemService(LightsManager.class);
mSensorManager = mContext.getSystemService(SensorManager.class);
-
- mDayColor = mContext.getColor(R.color.camera_privacy_light_day);
- mNightColor = mContext.getColor(R.color.camera_privacy_light_night);
mMovingAverageIntervalMillis = mContext.getResources()
.getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis);
- mNightThreshold = (long) (Math.log(mContext.getResources()
- .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold))
- * LIGHT_VALUE_MULTIPLIER);
+ int[] thresholdsLux = mContext.getResources().getIntArray(
+ R.array.config_cameraPrivacyLightAlsLuxThresholds);
+ if (thresholdsLux.length != mColors.length - 1) {
+ throw new IllegalStateException("There must be exactly one more color than thresholds."
+ + " Found " + mColors.length + " colors and " + thresholdsLux.length
+ + " thresholds.");
+ }
+ mThresholds = new long[thresholdsLux.length];
+ for (int i = 0; i < thresholdsLux.length; i++) {
+ int luxValue = thresholdsLux[i];
+ mThresholds[i] = (long) (Math.log(luxValue) * LIGHT_VALUE_MULTIPLIER);
+ }
List<Light> lights = mLightsManager.getLights();
for (int i = 0; i < lights.size(); i++) {
@@ -223,13 +238,8 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
mLightsSession.close();
mLightsSession = null;
} else {
- int lightColor;
- if (mLightSensor != null && getLiveAmbientLightTotal()
- < getCurrentIntervalMillis() * mNightThreshold) {
- lightColor = mNightColor;
- } else {
- lightColor = mDayColor;
- }
+ int lightColor =
+ mLightSensor == null ? mColors[mColors.length - 1] : computeCurrentLightColor();
if (mLastLightColor == lightColor && mLightsSession != null) {
return;
@@ -252,6 +262,18 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
}
}
+ private int computeCurrentLightColor() {
+ long liveAmbientLightTotal = getLiveAmbientLightTotal();
+ long currentInterval = getCurrentIntervalMillis();
+
+ for (int i = 0; i < mThresholds.length; i++) {
+ if (liveAmbientLightTotal < currentInterval * mThresholds[i]) {
+ return mColors[i];
+ }
+ }
+ return mColors[mColors.length - 1];
+ }
+
private void updateSensorListener(boolean shouldSessionEnd) {
if (shouldSessionEnd && mIsAmbientLightListenerRegistered) {
mSensorManager.unregisterListener(this);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9eec5f8c4bb6..ead7f1bf923a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2698,7 +2698,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void requestCopySplashScreen() {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_COPYING;
- if (!mAtmService.mTaskOrganizerController.copySplashScreenView(getTask())) {
+ if (mStartingSurface == null || !mAtmService.mTaskOrganizerController.copySplashScreenView(
+ getTask(), mStartingSurface.mTaskOrganizer)) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
removeStartingWindow();
}
@@ -2711,12 +2712,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
void onCopySplashScreenFinish(SplashScreenViewParcelable parcelable) {
removeTransferSplashScreenTimeout();
- // unable to copy from shell, maybe it's not a splash screen. or something went wrong.
- // either way, abort and reset the sequence.
- if (parcelable == null
+ final SurfaceControl windowAnimationLeash = (parcelable == null
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
|| mStartingWindow == null || mStartingWindow.mRemoved
- || finishing) {
+ || finishing) ? null
+ : TaskOrganizerController.applyStartingWindowAnimation(mStartingWindow);
+ if (windowAnimationLeash == null) {
+ // Unable to copy from shell, maybe it's not a splash screen, or something went wrong.
+ // Either way, abort and reset the sequence.
if (parcelable != null) {
parcelable.clearIfNeeded();
}
@@ -2724,9 +2727,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
removeStartingWindow();
return;
}
- // schedule attach splashScreen to client
- final SurfaceControl windowAnimationLeash = TaskOrganizerController
- .applyStartingWindowAnimation(mStartingWindow);
try {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
@@ -2766,7 +2766,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& (mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
|| mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE)) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Cleaning splash screen token=%s", this);
- mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask());
+ mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask(),
+ mStartingSurface != null ? mStartingSurface.mTaskOrganizer : null);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 59159bb1e393..42c363085017 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4048,10 +4048,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mRecentTasks.notifyTaskPersisterLocked(task, flush);
}
- boolean isKeyguardLocked(int displayId) {
- return mKeyguardController.isKeyguardLocked(displayId);
- }
-
/**
* Clears launch params for the given package.
*
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 8c1d8fa93d88..1a319ad61116 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -423,7 +423,7 @@ class KeyguardController {
final TransitionController tc = mRootWindowContainer.mTransitionController;
- final boolean occluded = isDisplayOccluded(displayId);
+ final boolean occluded = getDisplayState(displayId).mOccluded;
final boolean performTransition = isKeyguardLocked(displayId);
final boolean executeTransition = performTransition && !tc.isCollecting();
@@ -500,15 +500,6 @@ class KeyguardController {
}
}
- /**
- * Returns {@code true} if the top activity on the display can occlude keyguard or the device
- * is dreaming. Note that this method may return {@code true} even if the keyguard is disabled
- * or not showing.
- */
- boolean isDisplayOccluded(int displayId) {
- return getDisplayState(displayId).mOccluded;
- }
-
ActivityRecord getTopOccludingActivity(int displayId) {
return getDisplayState(displayId).mTopOccludesActivity;
}
@@ -601,6 +592,11 @@ class KeyguardController {
private boolean mAodShowing;
private boolean mKeyguardGoingAway;
private boolean mDismissalRequested;
+
+ /**
+ * True if the top activity on the display can occlude keyguard or the device is dreaming.
+ * Note that this can be true even if the keyguard is disabled or not showing.
+ */
private boolean mOccluded;
private boolean mShowingDream;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6b4cc25e62ca..57f8268b2fdc 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2699,7 +2699,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// transition exists, so this affects only when no lock screen is set. Otherwise
// keyguard going away animation will be played.
// See also AppTransitionController#getTransitCompatType for more details.
- if ((!mTaskSupervisor.getKeyguardController().isDisplayOccluded(display.mDisplayId)
+ if ((!mTaskSupervisor.getKeyguardController().isKeyguardOccluded(display.mDisplayId)
&& token.mTag.equals(KEYGUARD_SLEEP_TOKEN_TAG))
|| token.mTag.equals(DISPLAY_OFF_SLEEP_TOKEN_TAG)) {
display.mSkipAppTransitionAnimation = true;
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 0bb773ae5e41..a55c232990cf 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -40,6 +40,7 @@ import android.compat.annotation.EnabledSince;
import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
import android.util.Slog;
+import android.window.ITaskOrganizer;
import android.window.SplashScreenView;
import android.window.TaskSnapshot;
@@ -79,12 +80,13 @@ public class StartingSurfaceController {
}
StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, int theme) {
-
synchronized (mService.mGlobalLock) {
final Task task = activity.getTask();
- if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(
- task, activity, theme, null /* taskSnapshot */)) {
- return new StartingSurface(task);
+ final TaskOrganizerController controller =
+ mService.mAtmService.mTaskOrganizerController;
+ if (task != null && controller.addStartingWindow(task, activity, theme,
+ null /* taskSnapshot */)) {
+ return new StartingSurface(task, controller.getTaskOrganizer());
}
}
return null;
@@ -166,9 +168,12 @@ public class StartingSurfaceController {
activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
activity, false /* checkOpening */);
}
- mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
- activity, 0 /* launchTheme */, taskSnapshot);
- return new StartingSurface(task);
+ final TaskOrganizerController controller =
+ mService.mAtmService.mTaskOrganizerController;
+ if (controller.addStartingWindow(task, activity, 0 /* launchTheme */, taskSnapshot)) {
+ return new StartingSurface(task, controller.getTaskOrganizer());
+ }
+ return null;
}
}
@@ -256,9 +261,12 @@ public class StartingSurfaceController {
final class StartingSurface {
private final Task mTask;
+ // The task organizer which hold the client side reference of this surface.
+ final ITaskOrganizer mTaskOrganizer;
- StartingSurface(Task task) {
+ StartingSurface(Task task, ITaskOrganizer taskOrganizer) {
mTask = task;
+ mTaskOrganizer = taskOrganizer;
}
/**
@@ -268,7 +276,8 @@ public class StartingSurfaceController {
*/
public void remove(boolean animate) {
synchronized (mService.mGlobalLock) {
- mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, animate);
+ mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
+ mTaskOrganizer, animate);
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f9bbc6810835..387a8767ced3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3579,7 +3579,7 @@ class Task extends TaskFragment {
&& activity.info != info.taskInfo.topActivityInfo
? activity.info : null;
info.isKeyguardOccluded =
- mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
+ mAtmService.mKeyguardController.isKeyguardOccluded(info.taskInfo.displayId);
info.startingWindowTypeParameter = activity.mStartingData != null
? activity.mStartingData.mTypeParams
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 3d01001a9f37..41e49b9f0466 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -652,7 +652,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
if (rootTask == null || activity.mStartingData == null) {
return false;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = getTaskOrganizer();
if (lastOrganizer == null) {
return false;
}
@@ -672,12 +672,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return true;
}
- void removeStartingWindow(Task task, boolean prepareAnimation) {
+ void removeStartingWindow(Task task, ITaskOrganizer taskOrganizer, boolean prepareAnimation) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = taskOrganizer != null ? taskOrganizer
+ : getTaskOrganizer();
if (lastOrganizer == null) {
return;
}
@@ -771,12 +772,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
- boolean copySplashScreenView(Task task) {
+ boolean copySplashScreenView(Task task, ITaskOrganizer taskOrganizer) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return false;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = taskOrganizer != null ? taskOrganizer
+ : getTaskOrganizer();
if (lastOrganizer == null) {
return false;
}
@@ -799,12 +801,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
* @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
* @see SplashScreenView#remove()
*/
- public void onAppSplashScreenViewRemoved(Task task) {
+ public void onAppSplashScreenViewRemoved(Task task, ITaskOrganizer organizer) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = organizer != null ? organizer : getTaskOrganizer();
if (lastOrganizer == null) {
return;
}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index ea5ce65f6f79..49962eabbff3 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -52,7 +52,6 @@ public final class NetworkConstants {
public static final int IPV6_ADDR_BITS = 128;
public static final int IPV6_ADDR_LEN = 16;
public static final int IPV6_MIN_MTU = 1280;
- public static final int RFC7421_PREFIX_LENGTH = 64;
/**
* ICMP common (v4/v6) constants.
diff --git a/services/permission/TEST_MAPPING b/services/permission/TEST_MAPPING
index b2dcf379fe7d..24323c8bfbde 100644
--- a/services/permission/TEST_MAPPING
+++ b/services/permission/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.BackgroundPermissionsTest"
@@ -32,7 +32,7 @@
"name": "CtsPermissionPolicyTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
index 20cfd59973c3..dc04b6aea318 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -24,10 +24,12 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.AppOpsManager;
import android.content.Context;
@@ -43,14 +45,13 @@ import android.hardware.lights.LightsRequest;
import android.os.Handler;
import android.os.Looper;
import android.permission.PermissionManager;
-import android.util.ArraySet;
+import android.testing.TestableLooper;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.R;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -62,24 +63,19 @@ import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class CameraPrivacyLightControllerTest {
+ private int[] mDefaultColors = {0, 1, 2};
+ private int[] mDefaultAlsThresholdsLux = {10, 50};
+ private int mDefaultAlsAveragingIntervalMillis = 5000;
- private int mDayColor = 1;
- private int mNightColor = 0;
- private int mCameraPrivacyLightAlsAveragingIntervalMillis = 5000;
- private int mCameraPrivacyLightAlsNightThreshold = (int) getLightSensorValue(15);
+ private TestableLooper mTestableLooper;
private MockitoSession mMockitoSession;
@Mock
- private Context mContext;
-
- @Mock
- private Resources mResources;
-
- @Mock
private LightsManager mLightsManager;
@Mock
@@ -103,61 +99,98 @@ public class CameraPrivacyLightControllerTest {
private ArgumentCaptor<SensorEventListener> mLightSensorListenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
- private Set<String> mExemptedPackages = new ArraySet<>();
- private List<Light> mLights = new ArrayList<>();
+ private Set<String> mExemptedPackages;
+ private List<Light> mLights;
private int mNextLightId = 1;
- @Before
- public void setUp() {
- mMockitoSession = ExtendedMockito.mockitoSession()
- .initMocks(this)
- .strictness(Strictness.WARN)
- .spyStatic(PermissionManager.class)
- .startMocking();
+ public CameraPrivacyLightController prepareDefaultCameraPrivacyLightController() {
+ return prepareDefaultCameraPrivacyLightController(List.of(getNextLight(true)));
+ }
- doReturn(mDayColor).when(mContext).getColor(R.color.camera_privacy_light_day);
- doReturn(mNightColor).when(mContext).getColor(R.color.camera_privacy_light_night);
+ public CameraPrivacyLightController prepareDefaultCameraPrivacyLightController(
+ List<Light> lights) {
+ return prepareCameraPrivacyLightController(lights, Set.of(), true, mDefaultColors,
+ mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
+ }
+
+ public CameraPrivacyLightController prepareCameraPrivacyLightController(List<Light> lights,
+ Set<String> exemptedPackages, boolean hasLightSensor, int[] lightColors,
+ int[] alsThresholds, int averagingInterval) {
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ Looper.prepare();
+ looper = Looper.myLooper();
+ }
+ if (mTestableLooper == null) {
+ try {
+ mTestableLooper = new TestableLooper(looper);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
- doReturn(mResources).when(mContext).getResources();
- doReturn(mCameraPrivacyLightAlsAveragingIntervalMillis).when(mResources)
+ Context context = mock(Context.class);
+ Resources resources = mock(Resources.class);
+ doReturn(resources).when(context).getResources();
+ doReturn(lightColors).when(resources).getIntArray(R.array.config_cameraPrivacyLightColors);
+ doReturn(alsThresholds).when(resources)
+ .getIntArray(R.array.config_cameraPrivacyLightAlsLuxThresholds);
+ doReturn(averagingInterval).when(resources)
.getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis);
- doReturn(mCameraPrivacyLightAlsNightThreshold).when(mResources)
- .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold);
- doReturn(mLightsManager).when(mContext).getSystemService(LightsManager.class);
- doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
- doReturn(mSensorManager).when(mContext).getSystemService(SensorManager.class);
+ doReturn(mLightsManager).when(context).getSystemService(LightsManager.class);
+ doReturn(mAppOpsManager).when(context).getSystemService(AppOpsManager.class);
+ doReturn(mSensorManager).when(context).getSystemService(SensorManager.class);
+ mLights = lights;
+ mExemptedPackages = exemptedPackages;
doReturn(mLights).when(mLightsManager).getLights();
doReturn(mLightsSession).when(mLightsManager).openSession(anyInt());
+ if (!hasLightSensor) {
+ mLightSensor = null;
+ }
doReturn(mLightSensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- doReturn(mExemptedPackages)
+ doReturn(exemptedPackages)
.when(() -> PermissionManager.getIndicatorExemptedPackages(any()));
+
+ return new CameraPrivacyLightController(context, looper);
+ }
+
+ @Before
+ public void setUp() {
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(PermissionManager.class)
+ .startMocking();
}
@After
public void tearDown() {
- mExemptedPackages.clear();
- mLights.clear();
-
mMockitoSession.finishMocking();
}
@Test
+ public void testNoInteractionsWithServicesIfNoColorsSpecified() {
+ prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET,
+ true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
+
+ verifyZeroInteractions(mLightsManager);
+ verifyZeroInteractions(mAppOpsManager);
+ verifyZeroInteractions(mSensorManager);
+ }
+
+ @Test
public void testAppsOpsListenerNotRegisteredWithoutCameraLights() {
- mLights.add(getNextLight(false));
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController(List.of(getNextLight(false)));
verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any());
}
@Test
public void testAppsOpsListenerRegisteredWithCameraLight() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController();
verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any());
}
@@ -165,11 +198,12 @@ public class CameraPrivacyLightControllerTest {
@Test
public void testAllCameraLightsAreRequestedOnOpActive() {
Random r = new Random(0);
+ List<Light> lights = new ArrayList<>();
for (int i = 0; i < 30; i++) {
- mLights.add(getNextLight(r.nextBoolean()));
+ lights.add(getNextLight(r.nextBoolean()));
}
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController(lights);
// Verify no session has been opened at this point.
verify(mLightsManager, times(0)).openSession(anyInt());
@@ -181,8 +215,6 @@ public class CameraPrivacyLightControllerTest {
verify(mLightsManager, times(1)).openSession(anyInt());
verify(mLightsSession).requestLights(mLightsRequestCaptor.capture());
- assertEquals("requestLights() not invoked exactly once",
- 1, mLightsRequestCaptor.getAllValues().size());
List<Integer> expectedCameraLightIds = mLights.stream()
.filter(l -> l.getType() == Light.LIGHT_TYPE_CAMERA)
@@ -199,40 +231,25 @@ public class CameraPrivacyLightControllerTest {
@Test
public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
-
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ prepareDefaultCameraPrivacyLightController();
+ notifyCamOpChanged(10101, "pkg1", true);
verify(mLightsManager, times(1)).openSession(anyInt());
- listener.onOpActiveChanged(OPSTR_CAMERA, 10102, "pkg2", true);
+ notifyCamOpChanged(10102, "pkg2", true);
verify(mLightsManager, times(1)).openSession(anyInt());
}
@Test
public void testWillCloseOnFinishOp() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
-
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
-
+ prepareDefaultCameraPrivacyLightController();
+ notifyCamOpChanged(10101, "pkg1", true);
verify(mLightsSession, times(0)).close();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", false);
+ notifyCamOpChanged(10101, "pkg1", false);
verify(mLightsSession, times(1)).close();
}
@Test
public void testWillCloseOnFinishOpForAllPackages() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController();
int numUids = 100;
List<Integer> uids = new ArrayList<>(numUids);
@@ -240,64 +257,52 @@ public class CameraPrivacyLightControllerTest {
uids.add(10001 + i);
}
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
-
for (int i = 0; i < numUids; i++) {
- listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), true);
+ notifyCamOpChanged(uids.get(i), "pkg" + (int) uids.get(i), true);
}
// Change the order which their ops are finished
Collections.shuffle(uids, new Random(0));
for (int i = 0; i < numUids - 1; i++) {
- listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), false);
+ notifyCamOpChanged(uids.get(i), "pkg" + (int) uids.get(i), false);
}
verify(mLightsSession, times(0)).close();
int lastUid = uids.get(numUids - 1);
- listener.onOpActiveChanged(OPSTR_CAMERA, lastUid, "pkg" + lastUid, false);
+ notifyCamOpChanged(lastUid, "pkg" + lastUid, false);
verify(mLightsSession, times(1)).close();
}
@Test
public void testWontOpenForExemptedPackage() {
- mLights.add(getNextLight(true));
- mExemptedPackages.add("pkg1");
-
- createCameraPrivacyLightController();
+ String exemptPackage = "pkg1";
+ prepareCameraPrivacyLightController(List.of(getNextLight(true)),
+ Set.of(exemptPackage), true, mDefaultColors, mDefaultAlsThresholdsLux,
+ mDefaultAlsAveragingIntervalMillis);
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ notifyCamOpChanged(10101, exemptPackage, true);
verify(mLightsManager, times(0)).openSession(anyInt());
}
@Test
public void testNoLightSensor() {
- mLights.add(getNextLight(true));
- doReturn(null).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- createCameraPrivacyLightController();
+ prepareCameraPrivacyLightController(List.of(getNextLight(true)),
+ Set.of(), true, mDefaultColors, mDefaultAlsThresholdsLux,
+ mDefaultAlsAveragingIntervalMillis);
openCamera();
verify(mLightsSession).requestLights(mLightsRequestCaptor.capture());
LightsRequest lightsRequest = mLightsRequestCaptor.getValue();
for (LightState lightState : lightsRequest.getLightStates()) {
- assertEquals(mDayColor, lightState.getColor());
+ assertEquals(mDefaultColors[mDefaultColors.length - 1], lightState.getColor());
}
}
@Test
public void testALSListenerNotRegisteredUntilCameraIsOpened() {
- mLights.add(getNextLight(true));
- Sensor sensor = mock(Sensor.class);
- doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- CameraPrivacyLightController cplc = createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController();
verify(mSensorManager, never()).registerListener(any(SensorEventListener.class),
any(Sensor.class), anyInt(), any(Handler.class));
@@ -307,113 +312,44 @@ public class CameraPrivacyLightControllerTest {
verify(mSensorManager, times(1)).registerListener(mLightSensorListenerCaptor.capture(),
any(Sensor.class), anyInt(), any(Handler.class));
- mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", false);
+ notifyCamOpChanged(10001, "pkg", false);
verify(mSensorManager, times(1)).unregisterListener(mLightSensorListenerCaptor.getValue());
}
- @Ignore
- @Test
- public void testDayColor() {
- testBrightnessToColor(20, mDayColor);
- }
-
- @Ignore
@Test
- public void testNightColor() {
- testBrightnessToColor(10, mNightColor);
- }
-
- private void testBrightnessToColor(int brightnessValue, int color) {
- mLights.add(getNextLight(true));
- Sensor sensor = mock(Sensor.class);
- doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- CameraPrivacyLightController cplc = createCameraPrivacyLightController();
+ public void testAlsThresholds() {
+ CameraPrivacyLightController cplc = prepareDefaultCameraPrivacyLightController();
+ long elapsedTime = 0;
cplc.setElapsedRealTime(0);
-
openCamera();
-
- verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(),
- any(Sensor.class), anyInt(), any(Handler.class));
- SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue();
- float[] sensorEventValues = new float[1];
- SensorEvent sensorEvent = new SensorEvent(sensor, 0, 0, sensorEventValues);
-
- sensorEventValues[0] = getLightSensorValue(brightnessValue);
- sensorListener.onSensorChanged(sensorEvent);
-
- verify(mLightsSession, atLeastOnce()).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(color, lightState.getColor());
+ for (int i = 0; i < mDefaultColors.length; i++) {
+ int expectedColor = mDefaultColors[i];
+ int alsLuxValue = i
+ == mDefaultAlsThresholdsLux.length
+ ? mDefaultAlsThresholdsLux[i - 1] : mDefaultAlsThresholdsLux[i] - 1;
+
+ notifySensorEvent(cplc, elapsedTime, alsLuxValue);
+ elapsedTime += mDefaultAlsAveragingIntervalMillis + 1;
+ notifySensorEvent(cplc, elapsedTime, alsLuxValue);
+
+ verify(mLightsSession, atLeastOnce()).requestLights(mLightsRequestCaptor.capture());
+ for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
+ assertEquals(expectedColor, lightState.getColor());
+ }
}
}
- @Ignore
- @Test
- public void testDayToNightTransistion() {
- mLights.add(getNextLight(true));
- Sensor sensor = mock(Sensor.class);
- doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- CameraPrivacyLightController cplc = createCameraPrivacyLightController();
- cplc.setElapsedRealTime(0);
-
- openCamera();
- // There will be an initial call at brightness 0
- verify(mLightsSession, times(1)).requestLights(any(LightsRequest.class));
-
- verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(),
- any(Sensor.class), anyInt(), any(Handler.class));
- SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue();
-
- onSensorEvent(cplc, sensorListener, sensor, 0, 20);
-
- // 5 sec avg = 20
- onSensorEvent(cplc, sensorListener, sensor, 5000, 30);
-
- verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(mDayColor, lightState.getColor());
- }
-
- // 5 sec avg = 22
-
- onSensorEvent(cplc, sensorListener, sensor, 6000, 10);
-
- // 5 sec avg = 18
-
- onSensorEvent(cplc, sensorListener, sensor, 8000, 5);
-
- // Should have always been day
- verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(mDayColor, lightState.getColor());
- }
-
- // 5 sec avg = 12
-
- onSensorEvent(cplc, sensorListener, sensor, 10000, 5);
-
- // Should now be night
- verify(mLightsSession, times(3)).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(mNightColor, lightState.getColor());
- }
+ private void notifyCamOpChanged(int uid, String pkg, boolean active) {
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+ mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, uid, pkg, active);
}
- private void onSensorEvent(CameraPrivacyLightController cplc,
- SensorEventListener sensorListener, Sensor sensor, long timestamp, int value) {
+ private void notifySensorEvent(CameraPrivacyLightController cplc, long timestamp, int value) {
cplc.setElapsedRealTime(timestamp);
- sensorListener.onSensorChanged(new SensorEvent(sensor, 0, timestamp,
- new float[] {getLightSensorValue(value)}));
- }
-
- // Use the test thread so that the test is deterministic
- private CameraPrivacyLightController createCameraPrivacyLightController() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- return new CameraPrivacyLightController(mContext, Looper.myLooper());
+ verify(mSensorManager, atLeastOnce()).registerListener(mLightSensorListenerCaptor.capture(),
+ eq(mLightSensor), anyInt(), any());
+ mLightSensorListenerCaptor.getValue().onSensorChanged(new SensorEvent(mLightSensor, 0,
+ TimeUnit.MILLISECONDS.toNanos(timestamp), new float[] {value}));
}
private Light getNextLight(boolean cameraType) {
@@ -427,10 +363,6 @@ public class CameraPrivacyLightControllerTest {
return light;
}
- private float getLightSensorValue(int i) {
- return (float) Math.exp(i / CameraPrivacyLightController.LIGHT_VALUE_MULTIPLIER);
- }
-
private void openCamera() {
verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", true);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index 64e776e35f00..a621c0c01067 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -181,6 +181,7 @@ public class AuthenticationStatsCollectorTest {
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
}
@@ -203,6 +204,8 @@ public class AuthenticationStatsCollectorTest {
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(500);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(400);
+ assertThat(authenticationStats.getEnrollmentNotifications())
+ .isEqualTo(MAXIMUM_ENROLLMENT_NOTIFICATIONS);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(0.8f);
}
@@ -230,6 +233,7 @@ public class AuthenticationStatsCollectorTest {
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
}
@@ -256,6 +260,7 @@ public class AuthenticationStatsCollectorTest {
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
}
@@ -284,6 +289,8 @@ public class AuthenticationStatsCollectorTest {
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
+ // Assert that notification count has been updated.
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(1);
}
@Test
@@ -311,5 +318,7 @@ public class AuthenticationStatsCollectorTest {
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
+ // Assert that notification count has been updated.
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(1);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
index dde2a3c2cfd7..0c0d47a6b165 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
@@ -217,6 +217,31 @@ public class AuthenticationStatsPersisterTest {
}
@Test
+ public void persistFrrStats_multiUser_newUser_shouldUpdateRecord() throws JSONException {
+ AuthenticationStats authenticationStats1 = new AuthenticationStats(USER_ID_1,
+ 300 /* totalAttempts */, 10 /* rejectedAttempts */,
+ 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE);
+ AuthenticationStats authenticationStats2 = new AuthenticationStats(USER_ID_2,
+ 100 /* totalAttempts */, 5 /* rejectedAttempts */,
+ 1 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+
+ // Sets up the shared preference with user 1 only.
+ when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn(
+ Set.of(buildFrrStats(authenticationStats1)));
+
+ // Add data for user 2.
+ mAuthenticationStatsPersister.persistFrrStats(authenticationStats2.getUserId(),
+ authenticationStats2.getTotalAttempts(),
+ authenticationStats2.getRejectedAttempts(),
+ authenticationStats2.getEnrollmentNotifications(),
+ authenticationStats2.getModality());
+
+ verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture());
+ assertThat(mStringSetArgumentCaptor.getValue())
+ .contains(buildFrrStats(authenticationStats2));
+ }
+
+ @Test
public void removeFrrStats_existingUser_shouldUpdateRecord() throws JSONException {
AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1,
300 /* totalAttempts */, 10 /* rejectedAttempts */,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 37f49838a61b..70e5c2e1b198 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -83,7 +83,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
@@ -128,7 +127,6 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static java.util.Collections.emptyList;
@@ -596,9 +594,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mAcquiredWakeLocks.add(wl);
return wl;
});
- mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false);
// apps allowed as convos
mService.setStringArrayResourceValue(PKG_O);
@@ -1964,34 +1959,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void enqueueNotification_wakeLockSystemPropertyOff_noWakeLock() throws Exception {
- mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG,
- "enqueueNotification_setsWakeLockWorkSource", 0,
- generateNotificationRecord(null).getNotification(), 0);
- waitForIdle();
-
- verifyZeroInteractions(mPowerManager);
- }
-
- @Test
- public void enqueueNotification_wakeLockDeviceConfigOff_noWakeLock() throws Exception {
- mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "false", false);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG,
- "enqueueNotification_setsWakeLockWorkSource", 0,
- generateNotificationRecord(null).getNotification(), 0);
- waitForIdle();
-
- verifyZeroInteractions(mPowerManager);
- }
-
- @Test
public void testCancelNonexistentNotification() throws Exception {
mBinderService.cancelNotificationWithTag(PKG, PKG,
"testCancelNonexistentNotification", 0, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 302ad7f33b7c..31682bc9e879 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1541,7 +1541,8 @@ public class ActivityRecordTests extends WindowTestsBase {
// Make keyguard locked and set the top activity show-when-locked.
KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
int displayId = activity.getDisplayId();
- doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId));
+ keyguardController.setKeyguardShown(displayId, true /* keyguardShowing */,
+ false /* aodShowing */);
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.setVisibleRequested(true);
topActivity.nowVisible = true;
@@ -1553,7 +1554,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Verify the stack-top activity is occluded keyguard.
assertEquals(topActivity, task.topRunningActivity());
- assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ assertTrue(keyguardController.isKeyguardOccluded(displayId));
// Finish the top activity
topActivity.setState(PAUSED, "true");
@@ -1562,7 +1563,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Verify new top activity does not occlude keyguard.
assertEquals(activity, task.topRunningActivity());
- assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ assertFalse(keyguardController.isKeyguardOccluded(displayId));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d169a5854699..5341588c3992 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -24,7 +24,6 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
@@ -41,11 +40,9 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,7 +57,6 @@ import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
-import android.os.IBinder;
import android.os.LocaleList;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -76,7 +72,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
-import org.mockito.MockitoSession;
import java.util.ArrayList;
import java.util.List;
@@ -304,18 +299,11 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
*/
@Test
public void testEnterPipModeWhenRecordParentChangesToNull() {
- MockitoSession mockSession = mockitoSession()
- .initMocks(this)
- .mockStatic(ActivityRecord.class)
- .startMocking();
-
- ActivityRecord record = mock(ActivityRecord.class);
- IBinder token = mock(IBinder.class);
+ final ActivityRecord record = new ActivityBuilder(mAtm).setCreateTask(true).build();
PictureInPictureParams params = mock(PictureInPictureParams.class);
record.pictureInPictureArgs = params;
//mock operations in private method ensureValidPictureInPictureActivityParamsLocked()
- when(ActivityRecord.forTokenLocked(token)).thenReturn(record);
doReturn(true).when(record).supportsPictureInPicture();
doReturn(false).when(params).hasSetAspectRatio();
@@ -323,15 +311,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
doReturn(true).when(record)
.checkEnterPictureInPictureState("enterPictureInPictureMode", false);
doReturn(false).when(record).inPinnedWindowingMode();
- doReturn(false).when(mAtm).isKeyguardLocked(anyInt());
+ doReturn(false).when(record).isKeyguardLocked();
//to simulate NPE
doReturn(null).when(record).getParent();
- mAtm.mActivityClientController.enterPictureInPictureMode(token, params);
+ mAtm.mActivityClientController.enterPictureInPictureMode(record.token, params);
//if record's null parent is not handled gracefully, test will fail with NPE
-
- mockSession.finishMocking();
}
@Test