summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java89
-rw-r--r--config/dirty-image-objects417
-rw-r--r--core/java/android/app/ActivityOptions.java7
-rw-r--r--core/java/android/app/ITaskStackListener.aidl2
-rw-r--r--core/java/android/app/TaskStackListener.java12
-rw-r--r--core/java/android/credentials/ui/RequestInfo.java36
-rw-r--r--core/java/android/provider/Settings.java14
-rw-r--r--core/java/android/service/credentials/CredentialProviderInfoFactory.java22
-rw-r--r--core/java/android/service/credentials/CredentialProviderService.java24
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java15
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java28
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java41
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java9
-rw-r--r--core/java/com/android/internal/display/RefreshRateSettingsUtils.java92
-rw-r--r--core/proto/android/companion/telecom.proto10
-rw-r--r--core/res/res/values-mcc310/config.xml3
-rw-r--r--core/res/res/values-mcc311/config.xml3
-rw-r--r--core/res/res/values-mcc312/config.xml3
-rw-r--r--core/res/res/values-mcc313/config.xml3
-rw-r--r--core/res/res/values-mcc314/config.xml3
-rw-r--r--core/res/res/values-mcc315/config.xml3
-rw-r--r--core/res/res/values-mcc316/config.xml3
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/dimens.xml8
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--data/etc/services.core.protolog.json30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt149
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java108
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt238
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt139
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java15
-rw-r--r--packages/CompanionDeviceManager/res/layout/list_item_permission.xml1
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java24
-rw-r--r--packages/CredentialManager/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java5
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java53
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SystemUI/AndroidManifest.xml12
-rw-r--r--packages/SystemUI/OWNERS1
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java3
-rw-r--r--packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml21
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml1
-rw-r--r--packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml16
-rw-r--r--packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml20
-rw-r--r--packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml24
-rw-r--r--packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml27
-rw-r--r--packages/SystemUI/res/layout/combined_qs_header.xml3
-rw-r--r--packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml12
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json1
-rw-r--r--packages/SystemUI/res/values-sw720dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml51
-rw-r--r--packages/SystemUI/res/values/strings.xml18
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java7
-rw-r--r--packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt80
-rw-r--r--packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt113
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java163
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java17
-rw-r--r--packages/SystemUI/tests/AndroidManifest.xml11
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt89
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt140
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt89
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java17
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java5
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java59
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java31
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java187
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java380
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java (renamed from services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java)10
-rw-r--r--services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java57
-rw-r--r--services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java21
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java12
-rw-r--r--services/core/java/com/android/server/am/UserController.java3
-rw-r--r--services/core/java/com/android/server/am/UserSwitchingDialog.java63
-rw-r--r--services/core/java/com/android/server/audio/SoundDoseHelper.java75
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java47
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java53
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java1
-rw-r--r--services/core/java/com/android/server/policy/SingleKeyGestureDetector.java2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java54
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java4
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskChangeNotificationController.java6
-rw-r--r--services/core/java/com/android/server/wm/Transition.java49
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java91
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java1
-rw-r--r--services/credentials/java/com/android/server/credentials/CreateRequestSession.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java1
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java3
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java6
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java3
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java39
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java83
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java138
-rw-r--r--services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java175
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java11
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java6
192 files changed, 4550 insertions, 2110 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f779b4d96b45..b0b3b1f3cb5e 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -319,6 +319,8 @@ public class DeviceIdleController extends SystemService
private SensorManager mSensorManager;
private final boolean mUseMotionSensor;
private Sensor mMotionSensor;
+ private final boolean mIsLocationPrefetchEnabled;
+ @Nullable
private LocationRequest mLocationRequest;
private Intent mIdleIntent;
private Bundle mIdleIntentOptions;
@@ -2460,6 +2462,11 @@ public class DeviceIdleController extends SystemService
return null;
}
+ boolean isLocationPrefetchEnabled() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePrefetchLocation);
+ }
+
boolean useMotionSensor() {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
@@ -2489,6 +2496,7 @@ public class DeviceIdleController extends SystemService
mAppStateTracker = mInjector.getAppStateTracker(context,
AppSchedulingModuleThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
+ mIsLocationPrefetchEnabled = mInjector.isLocationPrefetchEnabled();
mUseMotionSensor = mInjector.useMotionSensor();
}
@@ -2602,8 +2610,7 @@ public class DeviceIdleController extends SystemService
mMotionSensor = mInjector.getMotionSensor();
}
- if (getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
+ if (mIsLocationPrefetchEnabled) {
mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0)
.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
.setMaxUpdates(1)
@@ -3779,34 +3786,40 @@ public class DeviceIdleController extends SystemService
case STATE_SENSING:
cancelSensingTimeoutAlarmLocked();
moveToStateLocked(STATE_LOCATING, reason);
- scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT);
- LocationManager locationManager = mInjector.getLocationManager();
- if (locationManager != null
- && locationManager.getProvider(LocationManager.FUSED_PROVIDER) != null) {
- locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
- mLocationRequest,
- AppSchedulingModuleThread.getExecutor(),
- mGenericLocationListener);
- mLocating = true;
- } else {
- mHasFusedLocation = false;
- }
- if (locationManager != null
- && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
- mHasGps = true;
- locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
- mGpsLocationListener, mHandler.getLooper());
- mLocating = true;
+ if (mIsLocationPrefetchEnabled) {
+ scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT);
+ LocationManager locationManager = mInjector.getLocationManager();
+ if (locationManager != null
+ && locationManager.getProvider(LocationManager.FUSED_PROVIDER)
+ != null) {
+ locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
+ mLocationRequest,
+ AppSchedulingModuleThread.getExecutor(),
+ mGenericLocationListener);
+ mLocating = true;
+ } else {
+ mHasFusedLocation = false;
+ }
+ if (locationManager != null
+ && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
+ mHasGps = true;
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
+ 1000, 5, mGpsLocationListener, mHandler.getLooper());
+ mLocating = true;
+ } else {
+ mHasGps = false;
+ }
+ // If we have a location provider, we're all set, the listeners will move state
+ // forward.
+ if (mLocating) {
+ break;
+ }
+ // Otherwise, we have to move from locating into idle maintenance.
} else {
- mHasGps = false;
- }
- // If we have a location provider, we're all set, the listeners will move state
- // forward.
- if (mLocating) {
- break;
+ mLocating = false;
}
- // Otherwise, we have to move from locating into idle maintenance.
+ // We're not doing any locating work, so move on to the next state.
case STATE_LOCATING:
cancelAlarmLocked();
cancelLocatingLocked();
@@ -5303,15 +5316,19 @@ public class DeviceIdleController extends SystemService
pw.print(" "); pw.print(mStationaryListeners.size());
pw.println(" stationary listeners registered");
}
- pw.print(" mLocating="); pw.print(mLocating);
- pw.print(" mHasGps="); pw.print(mHasGps);
- pw.print(" mHasFused="); pw.print(mHasFusedLocation);
- pw.print(" mLocated="); pw.println(mLocated);
- if (mLastGenericLocation != null) {
- pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation);
- }
- if (mLastGpsLocation != null) {
- pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation);
+ if (mIsLocationPrefetchEnabled) {
+ pw.print(" mLocating="); pw.print(mLocating);
+ pw.print(" mHasGps="); pw.print(mHasGps);
+ pw.print(" mHasFused="); pw.print(mHasFusedLocation);
+ pw.print(" mLocated="); pw.println(mLocated);
+ if (mLastGenericLocation != null) {
+ pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation);
+ }
+ if (mLastGpsLocation != null) {
+ pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation);
+ }
+ } else {
+ pw.println(" Location prefetching disabled");
}
pw.print(" mState="); pw.print(stateToString(mState));
pw.print(" mLightState=");
diff --git a/config/dirty-image-objects b/config/dirty-image-objects
index dfd091cc1aff..2584610e2848 100644
--- a/config/dirty-image-objects
+++ b/config/dirty-image-objects
@@ -28,359 +28,270 @@
# Then, grep for lines containing "Private dirty object" from the output.
# This particular file was generated by dumping systemserver and systemui.
#
-Landroid/accounts/Account;
-Landroid/accounts/OnAccountsUpdateListener;
Landroid/animation/LayoutTransition;
Landroid/app/ActivityManager;
-Landroid/app/ActivityManager$OnUidImportanceListener;
Landroid/app/ActivityTaskManager;
Landroid/app/ActivityThread;
-Landroid/app/admin/DevicePolicyManager;
Landroid/app/AlarmManager;
-Landroid/app/Application;
Landroid/app/AppOpsManager;
-Landroid/app/backup/BackupManager;
Landroid/app/ContextImpl;
-Landroid/app/INotificationManager;
-Landroid/app/Notification$BigPictureStyle;
-Landroid/app/Notification$BigTextStyle;
-Landroid/app/Notification$InboxStyle;
-Landroid/app/NotificationChannel;
-Landroid/app/NotificationChannelGroup;
+Landroid/app/Notification;
Landroid/app/NotificationManager;
-Landroid/app/PendingIntent;
-Landroid/app/PendingIntent$OnFinished;
+Landroid/app/PendingIntent$FinishedDispatcher;
+Landroid/app/PropertyInvalidatedCache$NoPreloadHolder;
Landroid/app/QueuedWork;
Landroid/app/ResourcesManager;
+Landroid/app/SystemServiceRegistry;
Landroid/app/WallpaperManager;
-Landroid/app/WindowConfiguration;
-Landroid/bluetooth/BluetoothAdapter;
-Landroid/bluetooth/BluetoothDevice;
-Landroid/bluetooth/BluetoothProfile;
-Landroid/bluetooth/IBluetoothA2dp;
-Landroid/bluetooth/IBluetoothHeadsetPhone;
-Landroid/bluetooth/IBluetoothHidDevice;
-Landroid/bluetooth/IBluetoothHidHost;
-Landroid/bluetooth/IBluetoothMap;
-Landroid/bluetooth/IBluetoothPan;
-Landroid/bluetooth/IBluetoothPbap;
-Landroid/bluetooth/IBluetoothSap;
-Landroid/content/ClipboardManager$OnPrimaryClipChangedListener;
-Landroid/content/ComponentName;
-Landroid/content/ContentProvider$PipeDataWriter;
+Landroid/app/backup/BackupManager;
+Landroid/compat/Compatibility;
+Landroid/content/AsyncQueryHandler;
+Landroid/content/ContentProviderClient;
Landroid/content/ContentResolver;
Landroid/content/Context;
-Landroid/content/Intent;
-Landroid/content/pm/PackageManager$OnPermissionsChangedListener;
-Landroid/content/pm/VersionedPackage;
-Landroid/content/res/Configuration;
-Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener;
+Landroid/content/pm/PackageItemInfo;
+Landroid/content/pm/UserPackage;
+Landroid/content/res/ResourceTimer;
Landroid/database/CursorWindow;
Landroid/database/sqlite/SQLiteCompatibilityWalFlags;
-Landroid/database/sqlite/SQLiteDatabase$CursorFactory;
+Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder;
Landroid/database/sqlite/SQLiteGlobal;
-Landroid/database/sqlite/SQLiteTransactionListener;
Landroid/ddm/DdmHandleAppName;
Landroid/graphics/Bitmap;
Landroid/graphics/Canvas;
-Landroid/graphics/drawable/AdaptiveIconDrawable;
-Landroid/graphics/drawable/ColorDrawable;
-Landroid/graphics/drawable/GradientDrawable;
-Landroid/graphics/drawable/Icon;
-Landroid/graphics/drawable/InsetDrawable;
-Landroid/graphics/drawable/RippleDrawable;
-Landroid/graphics/drawable/VectorDrawable$VGroup;
-Landroid/graphics/ImageDecoder;
-Landroid/graphics/Rect;
+Landroid/graphics/Compatibility;
+Landroid/graphics/HardwareRenderer;
Landroid/graphics/TemporaryBuffer;
-Landroid/hardware/biometrics/BiometricSourceType;
-Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal;
-Landroid/hardware/display/DisplayManagerGlobal;
-Landroid/hardware/display/NightDisplayListener$Callback;
-Landroid/hardware/input/InputManager;
-Landroid/hardware/input/InputManager$InputDeviceListener;
+Landroid/graphics/Typeface;
+Landroid/graphics/drawable/AdaptiveIconDrawable;
Landroid/hardware/SensorPrivacyManager;
Landroid/hardware/SystemSensorManager;
-Landroid/icu/impl/OlsonTimeZone;
-Landroid/icu/text/BreakIterator;
+Landroid/hardware/devicestate/DeviceStateManagerGlobal;
+Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal;
+Landroid/hardware/display/DisplayManagerGlobal;
+Landroid/hardware/input/InputManagerGlobal;
+Landroid/hardware/location/GeofenceHardwareImpl;
+Landroid/icu/impl/number/range/StandardPluralRanges;
Landroid/icu/text/Collator;
-Landroid/icu/text/DateFormat$BooleanAttribute;
-Landroid/icu/text/DateTimePatternGenerator$DTPGflags;
-Landroid/icu/text/PluralRules$Operand;
Landroid/icu/util/TimeZone;
-Landroid/location/GpsStatus$Listener;
-Landroid/location/LocationListener;
+Landroid/location/LocationManager;
Landroid/media/AudioManager;
+Landroid/media/AudioPlaybackConfiguration;
+Landroid/media/AudioSystem;
+Landroid/media/MediaCodec;
+Landroid/media/MediaCodecList;
+Landroid/media/MediaFrameworkPlatformInitializer;
+Landroid/media/MediaRouter2Manager;
Landroid/media/MediaRouter;
Landroid/media/PlayerBase;
-Landroid/media/session/MediaSessionManager;
-Landroid/net/apf/ApfCapabilities;
-Landroid/net/ConnectivityManager;
-Landroid/net/ConnectivityManager$OnNetworkActiveListener;
-Landroid/net/ConnectivityThread$Singleton;
-Landroid/net/IpConfiguration$IpAssignment;
-Landroid/net/IpConfiguration$ProxySettings;
-Landroid/net/IpPrefix;
-Landroid/net/LinkAddress;
-Landroid/net/LinkProperties;
-Landroid/net/Network;
-Landroid/net/NetworkCapabilities;
-Landroid/net/NetworkInfo;
-Landroid/net/NetworkInfo$State;
-Landroid/net/NetworkRequest;
-Landroid/net/NetworkRequest$Type;
-Landroid/net/RouteInfo;
-Landroid/net/StringNetworkSpecifier;
-Landroid/net/TrafficStats;
-Landroid/net/UidRange;
-Landroid/net/Uri$HierarchicalUri;
-Landroid/net/Uri$StringUri;
-Landroid/net/wifi/WifiManager;
-Landroid/net/wifi/WifiManager$SoftApCallback;
-Landroid/os/AsyncResult;
+Landroid/media/audiopolicy/AudioProductStrategy;
+Landroid/media/audiopolicy/AudioVolumeGroup;
+Landroid/nfc/NfcAdapter;
+Landroid/nfc/NfcFrameworkInitializer;
+Landroid/nfc/cardemulation/CardEmulation;
Landroid/os/AsyncTask;
+Landroid/os/BaseBundle;
+Landroid/os/Binder;
Landroid/os/BinderProxy;
-Landroid/os/Bundle;
-Landroid/os/DeadObjectException;
Landroid/os/Environment;
Landroid/os/FileObserver;
Landroid/os/Handler;
-Landroid/os/IDeviceIdleController;
Landroid/os/LocaleList;
Landroid/os/Looper;
Landroid/os/Message;
-Landroid/os/ParcelUuid;
+Landroid/os/NullVibrator;
+Landroid/os/Parcel;
Landroid/os/Process;
-Landroid/os/RecoverySystem;
Landroid/os/ServiceManager;
-Landroid/os/storage/StorageManager;
Landroid/os/StrictMode;
-Landroid/os/Trace;
+Landroid/os/UEventObserver;
+Landroid/os/UserManager;
Landroid/os/WorkSource;
-Landroid/os/WorkSource$WorkChain;
+Landroid/os/storage/StorageManager;
Landroid/permission/PermissionManager;
+Landroid/provider/DeviceConfigInitializer;
Landroid/provider/FontsContract;
-Landroid/provider/Settings$SettingNotFoundException;
+Landroid/provider/Settings;
+Landroid/renderscript/RenderScript;
Landroid/renderscript/RenderScriptCacheDir;
-Landroid/security/IKeyChainService;
-Landroid/security/keystore/AndroidKeyStoreProvider;
+Landroid/security/keystore2/KeyStoreCryptoOperationUtils;
Landroid/security/net/config/ApplicationConfig;
Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder;
-Landroid/telecom/PhoneAccountHandle;
+Landroid/security/net/config/UserCertificateSource$NoPreloadHolder;
+Landroid/telecom/Log;
+Landroid/telecom/TelecomManager;
Landroid/telephony/AnomalyReporter;
-Landroid/telephony/CellSignalStrengthCdma;
-Landroid/telephony/CellSignalStrengthGsm;
-Landroid/telephony/CellSignalStrengthLte;
-Landroid/telephony/CellSignalStrengthNr;
-Landroid/telephony/CellSignalStrengthTdscdma;
-Landroid/telephony/CellSignalStrengthWcdma;
-Landroid/telephony/DataSpecificRegistrationInfo;
-Landroid/telephony/emergency/EmergencyNumber;
-Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder;
-Landroid/telephony/ims/ImsMmTelManager$RegistrationCallback$RegistrationBinder;
-Landroid/telephony/ims/ImsReasonInfo;
-Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder;
-Landroid/telephony/ModemActivityInfo;
-Landroid/telephony/ModemInfo;
-Landroid/telephony/NetworkRegistrationInfo;
-Landroid/telephony/NetworkService;
+Landroid/telephony/TelephonyFrameworkInitializer;
+Landroid/telephony/TelephonyLocalConnection;
Landroid/telephony/TelephonyManager;
-Landroid/telephony/VoiceSpecificRegistrationInfo;
+Landroid/telephony/TelephonyRegistryManager;
+Landroid/text/DynamicLayout;
+Landroid/text/TextUtils;
Landroid/text/format/DateFormat;
+Landroid/text/format/DateUtils;
+Landroid/text/method/ArrowKeyMovementMethod;
+Landroid/text/method/LinkMovementMethod;
Landroid/text/method/SingleLineTransformationMethod;
-Landroid/text/Selection$MemoryTextWatcher;
-Landroid/text/SpanWatcher;
-Landroid/text/style/AlignmentSpan;
-Landroid/text/style/CharacterStyle;
-Landroid/text/style/LeadingMarginSpan;
-Landroid/text/style/LineBackgroundSpan;
-Landroid/text/style/LineHeightSpan;
-Landroid/text/style/MetricAffectingSpan;
-Landroid/text/style/ReplacementSpan;
-Landroid/text/style/SuggestionSpan;
-Landroid/text/style/TabStopSpan;
-Landroid/text/TextUtils;
-Landroid/text/TextWatcher;
-Landroid/transition/ChangeClipBounds;
-Landroid/transition/ChangeImageTransform;
-Landroid/transition/ChangeTransform;
+Landroid/text/style/ClickableSpan;
+Landroid/timezone/TelephonyLookup;
+Landroid/timezone/TimeZoneFinder;
Landroid/util/ArrayMap;
Landroid/util/ArraySet;
-Landroid/util/DisplayMetrics;
Landroid/util/EventLog;
-Landroid/util/Log;
-Landroid/util/Patterns;
-Landroid/view/AbsSavedState$1;
-Landroid/view/accessibility/AccessibilityManager;
-Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener;
-Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener;
-Landroid/view/accessibility/AccessibilityNodeIdManager;
-Landroid/view/autofill/AutofillManager;
-Landroid/view/autofill/Helper;
+Landroid/util/NtpTrustedTime;
Landroid/view/Choreographer;
-Landroid/view/inputmethod/InputMethodManager;
-Landroid/view/IWindowManager;
+Landroid/view/CrossWindowBlurListeners;
+Landroid/view/DisplayCutout;
+Landroid/view/KeyEvent;
+Landroid/view/MotionEvent;
Landroid/view/PointerIcon;
-Landroid/view/RemoteAnimationAdapter;
-Landroid/view/ThreadedRenderer;
+Landroid/view/RoundedCorners;
+Landroid/view/SurfaceControl;
Landroid/view/View;
-Landroid/view/View$OnHoverListener;
+Landroid/view/ViewGroup$TouchTarget;
Landroid/view/ViewRootImpl;
-Landroid/view/ViewStub;
-Landroid/view/ViewStub$OnInflateListener;
Landroid/view/ViewTreeObserver;
-Landroid/view/WindowManager$LayoutParams;
Landroid/view/WindowManagerGlobal;
-Landroid/widget/ActionMenuPresenter$OverflowMenuButton;
-Landroid/widget/ActionMenuView;
-Landroid/widget/Button;
-Landroid/widget/CheckBox;
-Landroid/widget/FrameLayout;
-Landroid/widget/ImageButton;
+Landroid/view/accessibility/AccessibilityManager;
+Landroid/view/accessibility/AccessibilityNodeIdManager;
+Landroid/view/autofill/Helper;
+Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker;
+Landroid/view/inputmethod/InputMethodManager;
+Landroid/webkit/CookieSyncManager;
+Landroid/webkit/WebView;
+Landroid/webkit/WebViewFactory;
+Landroid/webkit/WebViewZygote;
+Landroid/widget/AbsListView;
Landroid/widget/ImageView;
Landroid/widget/LinearLayout;
-Landroid/widget/RelativeLayout;
-Landroid/widget/SeekBar;
-Landroid/widget/Space;
-Landroid/widget/TextView;
-Landroid/widget/Toolbar;
-[B
-Lcom/android/ims/ImsManager;
+Landroid/widget/Toast;
+Landroid/window/SurfaceSyncGroup;
+Lcom/android/i18n/timezone/TelephonyLookup;
+Lcom/android/i18n/timezone/TimeZoneFinder;
+Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper;
+Lcom/android/internal/content/om/OverlayConfig;
+Lcom/android/internal/display/BrightnessSynchronizer;
+Lcom/android/internal/infra/AndroidFuture;
+Lcom/android/internal/inputmethod/ImeTracing;
+Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry;
+Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder;
+Lcom/android/internal/jank/InteractionJankMonitor;
Lcom/android/internal/logging/MetricsLogger;
Lcom/android/internal/os/BackgroundThread;
Lcom/android/internal/os/BinderInternal;
-Lcom/android/internal/os/BinderInternal$BinderProxyLimitListener;
+Lcom/android/internal/os/KernelCpuBpfTracking;
Lcom/android/internal/os/RuntimeInit;
Lcom/android/internal/os/SomeArgs;
-Lcom/android/internal/policy/DecorView;
-Lcom/android/internal/statusbar/IStatusBarService;
-Lcom/android/internal/telephony/AppSmsManager;
-Landroid/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;
-Lcom/android/internal/telephony/CarrierActionAgent;
-Lcom/android/internal/telephony/cat/CatService;
-Lcom/android/internal/telephony/cat/IconLoader;
-Lcom/android/internal/telephony/cat/RilMessageDecoder;
-Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager;
-Lcom/android/internal/telephony/cdma/EriManager;
-Lcom/android/internal/telephony/CellularNetworkValidator;
-Lcom/android/internal/telephony/CommandException;
-Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState;
-Lcom/android/internal/telephony/dataconnection/DataEnabledSettings;
-Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/euicc/EuiccCardController;
-Lcom/android/internal/telephony/euicc/EuiccController;
-Lcom/android/internal/telephony/GsmAlphabet;
-Lcom/android/internal/telephony/GsmCdmaCallTracker;
-Lcom/android/internal/telephony/GsmCdmaPhone;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;
-Lcom/android/internal/telephony/IccSmsInterfaceManager;
-Lcom/android/internal/telephony/ims/ImsResolver;
-Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;
-Lcom/android/internal/telephony/imsphone/ImsPhone;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;
-Lcom/android/internal/telephony/ims/RcsMessageStoreController;
+Lcom/android/internal/os/ZygoteInit;
+Lcom/android/internal/policy/AttributeCache;
+Lcom/android/internal/protolog/BaseProtoLogImpl;
+Lcom/android/internal/protolog/ProtoLogImpl;
+Lcom/android/internal/statusbar/NotificationVisibility;
+Lcom/android/internal/telephony/CellBroadcastServiceManager;
Lcom/android/internal/telephony/IntentBroadcaster;
-Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;
-Lcom/android/internal/telephony/metrics/TelephonyMetrics;
+Lcom/android/internal/telephony/MccTable;
Lcom/android/internal/telephony/MultiSimSettingController;
-Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierAttribute;
-Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierId;
-Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall;
-Lcom/android/internal/telephony/nano/TelephonyProto$SmsSession$Event;
-Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall;
-Lcom/android/internal/telephony/NitzStateMachine;
+Lcom/android/internal/telephony/PackageChangeReceiver;
Lcom/android/internal/telephony/PhoneConfigurationManager;
Lcom/android/internal/telephony/PhoneFactory;
-Lcom/android/internal/telephony/PhoneSwitcher;
Lcom/android/internal/telephony/ProxyController;
-Lcom/android/internal/telephony/RadioConfig;
-Lcom/android/internal/telephony/RIL;
Lcom/android/internal/telephony/RILRequest;
-Lcom/android/internal/telephony/RilWakelockInfo;
-Lcom/android/internal/telephony/ServiceStateTracker;
-Lcom/android/internal/telephony/SimActivationTracker;
+Lcom/android/internal/telephony/RadioConfig;
+Lcom/android/internal/telephony/RadioInterfaceCapabilityController;
Lcom/android/internal/telephony/SmsApplication;
Lcom/android/internal/telephony/SmsBroadcastUndelivered;
-Lcom/android/internal/telephony/SmsStorageMonitor;
-Lcom/android/internal/telephony/SmsUsageMonitor;
-Lcom/android/internal/telephony/SubscriptionController;
-Lcom/android/internal/telephony/SubscriptionInfoUpdater;
+Lcom/android/internal/telephony/SomeArgs;
Lcom/android/internal/telephony/TelephonyComponentFactory;
Lcom/android/internal/telephony/TelephonyDevController;
-Lcom/android/internal/telephony/TelephonyTester;
-Lcom/android/internal/telephony/uicc/AdnRecordCache;
-Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/cat/CatService;
+Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler;
+Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager;
+Lcom/android/internal/telephony/euicc/EuiccCardController;
+Lcom/android/internal/telephony/euicc/EuiccController;
+Lcom/android/internal/telephony/ims/ImsResolver;
+Lcom/android/internal/telephony/metrics/TelephonyMetrics;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession;
+Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall;
+Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall;
+Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo;
+Lcom/android/internal/telephony/satellite/PointingAppController;
+Lcom/android/internal/telephony/satellite/SatelliteModemInterface;
Lcom/android/internal/telephony/uicc/UiccController;
-Lcom/android/internal/telephony/uicc/UiccProfile;
Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher;
-Lcom/android/internal/telephony/uicc/UsimFileHandler;
-Lcom/android/internal/telephony/uicc/VoiceMailConstants;
-Lcom/android/internal/util/LatencyTracker;
-Lcom/android/internal/util/StateMachine$SmHandler;
-Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/okio/AsyncTimeout;
-Lcom/android/okhttp/okio/SegmentPool;
+Lcom/android/internal/util/ContrastColorUtil;
+Lcom/android/internal/view/WindowManagerPolicyThread;
+Lcom/android/org/bouncycastle/crypto/CryptoServicesRegistrar;
Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo;
Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo;
-Lcom/android/server/sip/SipWakeupTimer;
-Lcom/android/server/SystemConfig;
+Lcom/android/server/AppWidgetBackupBridge;
Ldalvik/system/BaseDexClassLoader;
Ldalvik/system/BlockGuard;
Ldalvik/system/CloseGuard;
Ldalvik/system/RuntimeHooks;
Ldalvik/system/SocketTagger;
-Ljava/io/BufferedReader;
-Ljava/lang/AssertionError;
-Ljava/lang/Boolean;
-Ljava/lang/Byte;
-Ljava/lang/Character;
-Ljava/lang/CharSequence;
-Ljava/lang/Class;
-Ljava/lang/IllegalAccessException;
-Ljava/lang/IllegalStateException;
-Ljava/lang/NoSuchMethodException;
-Ljava/lang/NullPointerException;
-Ljava/lang/Object;
-[Ljava/lang/Object;
-Ljava/lang/ref/FinalizerReference;
-Ljava/lang/Runnable;
-Ljava/lang/SecurityException;
-Ljava/lang/Short;
-[Ljava/lang/String;
+Ldalvik/system/VMRuntime;
+Ldalvik/system/ZipPathValidator;
+Ldalvik/system/ZygoteHooks;
Ljava/lang/System;
Ljava/lang/Thread;
Ljava/lang/Throwable;
-Ljava/lang/UnsatisfiedLinkError;
-Ljava/net/Inet6Address;
-Ljava/net/Socket;
-Ljava/net/SocketException;
+Ljava/lang/ref/FinalizerReference;
+Ljava/lang/ref/ReferenceQueue;
+Ljava/net/ResponseCache;
Ljava/nio/Bits;
Ljava/nio/charset/Charset;
-Ljava/security/interfaces/RSAPrivateKey;
Ljava/security/Provider;
Ljava/util/Collections;
-Ljava/util/concurrent/Executor;
Ljava/util/GregorianCalendar;
-Ljava/util/Locale;
Ljava/util/Locale$NoImagePreloadHolder;
+Ljava/util/Locale;
Ljava/util/Scanner;
-Ljava/util/Set;
Ljava/util/TimeZone;
+Ljava/util/concurrent/ForkJoinPool;
+Ljava/util/concurrent/ThreadLocalRandom;
+Ljavax/net/ServerSocketFactory;
Ljavax/net/SocketFactory;
-Ljavax/net/ssl/HttpsURLConnection;
Ljavax/net/ssl/HttpsURLConnection$NoPreloadHolder;
+Ljavax/net/ssl/HttpsURLConnection;
Ljavax/net/ssl/SSLContext;
-Ljavax/net/ssl/SSLSessionContext;
+Ljavax/net/ssl/SSLServerSocketFactory;
Ljavax/net/ssl/SSLSocketFactory;
Llibcore/io/Libcore;
-Llibcore/io/Memory;
Llibcore/net/NetworkSecurityPolicy;
-Llibcore/timezone/TimeZoneFinder;
-Lorg/apache/http/params/HttpParams;
Lsun/misc/Cleaner;
-Lsun/nio/ch/FileChannelImpl;
Lsun/nio/ch/FileChannelImpl$Unmapper;
-Lsun/nio/fs/UnixChannelFactory;
+Lsun/nio/ch/FileChannelImpl;
Lsun/security/jca/Providers;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 61d6787b4f85..2ae721648656 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -403,8 +403,11 @@ public class ActivityOptions extends ComponentOptions {
private static final String KEY_SPLASH_SCREEN_STYLE =
"android.activity.splashScreenStyle";
- /** See {@link #setTransientLaunch()}. */
- private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
+ /**
+ * See {@link #setTransientLaunch()}.
+ * @hide
+ */
+ public static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
/** see {@link #makeLaunchIntoPip(PictureInPictureParams)}. */
private static final String KEY_LAUNCH_INTO_PIP_PARAMS =
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 36e57620c9dd..3c6ff2865d04 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -137,7 +137,7 @@ oneway interface ITaskStackListener {
* activities inside it belong to a managed profile user, and that user has just
* been locked.
*/
- void onTaskProfileLocked(in ActivityManager.RunningTaskInfo taskInfo);
+ void onTaskProfileLocked(in ActivityManager.RunningTaskInfo taskInfo, int userId);
/**
* Called when a task snapshot got updated.
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 774bc06e1adb..0290cee94dc3 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -154,8 +154,18 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
}
@Override
+ public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId)
+ throws RemoteException {
+ onTaskProfileLocked(taskInfo);
+ }
+
+ /**
+ * @deprecated see {@link #onTaskProfileLocked(RunningTaskInfo, int)}
+ */
+ @Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void onTaskProfileLocked(RunningTaskInfo taskInfo) throws RemoteException {
+ public void onTaskProfileLocked(RunningTaskInfo taskInfo)
+ throws RemoteException {
}
@Override
diff --git a/core/java/android/credentials/ui/RequestInfo.java b/core/java/android/credentials/ui/RequestInfo.java
index 09d2db89a043..9ebb0585afd0 100644
--- a/core/java/android/credentials/ui/RequestInfo.java
+++ b/core/java/android/credentials/ui/RequestInfo.java
@@ -30,6 +30,8 @@ import com.android.internal.util.AnnotationValidations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Contains information about the request that initiated this UX flow.
@@ -64,6 +66,9 @@ public final class RequestInfo implements Parcelable {
@Nullable
private final CreateCredentialRequest mCreateCredentialRequest;
+ @NonNull
+ private final List<String> mDefaultProviderIds;
+
@Nullable
private final GetCredentialRequest mGetCredentialRequest;
@@ -83,7 +88,8 @@ public final class RequestInfo implements Parcelable {
@NonNull String appPackageName) {
return new RequestInfo(
token, TYPE_CREATE, appPackageName, createCredentialRequest, null,
- /*hasPermissionToOverrideDefault=*/ false);
+ /*hasPermissionToOverrideDefault=*/ false,
+ /*defaultProviderIds=*/ new ArrayList<>());
}
/**
@@ -94,10 +100,11 @@ public final class RequestInfo implements Parcelable {
@NonNull
public static RequestInfo newCreateRequestInfo(
@NonNull IBinder token, @NonNull CreateCredentialRequest createCredentialRequest,
- @NonNull String appPackageName, boolean hasPermissionToOverrideDefault) {
+ @NonNull String appPackageName, boolean hasPermissionToOverrideDefault,
+ @NonNull List<String> defaultProviderIds) {
return new RequestInfo(
token, TYPE_CREATE, appPackageName, createCredentialRequest, null,
- hasPermissionToOverrideDefault);
+ hasPermissionToOverrideDefault, defaultProviderIds);
}
/** Creates new {@code RequestInfo} for a get-credential flow. */
@@ -107,7 +114,8 @@ public final class RequestInfo implements Parcelable {
@NonNull String appPackageName) {
return new RequestInfo(
token, TYPE_GET, appPackageName, null, getCredentialRequest,
- /*hasPermissionToOverrideDefault=*/ false);
+ /*hasPermissionToOverrideDefault=*/ false,
+ /*defaultProviderIds=*/ new ArrayList<>());
}
@@ -149,6 +157,20 @@ public final class RequestInfo implements Parcelable {
}
/**
+ * Returns default provider identifier (flattened component name) configured from the user
+ * settings.
+ *
+ * Will only be possibly non-empty for the create use case. Not meaningful for the sign-in use
+ * case.
+ *
+ * @hide
+ */
+ @NonNull
+ public List<String> getDefaultProviderIds() {
+ return mDefaultProviderIds;
+ }
+
+ /**
* Returns the non-null GetCredentialRequest when the type of the request is {@link
* #TYPE_GET}, or null otherwise.
*/
@@ -161,13 +183,15 @@ public final class RequestInfo implements Parcelable {
@NonNull String appPackageName,
@Nullable CreateCredentialRequest createCredentialRequest,
@Nullable GetCredentialRequest getCredentialRequest,
- boolean hasPermissionToOverrideDefault) {
+ boolean hasPermissionToOverrideDefault,
+ @NonNull List<String> defaultProviderIds) {
mToken = token;
mType = type;
mAppPackageName = appPackageName;
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
mHasPermissionToOverrideDefault = hasPermissionToOverrideDefault;
+ mDefaultProviderIds = defaultProviderIds == null ? new ArrayList<>() : defaultProviderIds;
}
private RequestInfo(@NonNull Parcel in) {
@@ -188,6 +212,7 @@ public final class RequestInfo implements Parcelable {
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
mHasPermissionToOverrideDefault = in.readBoolean();
+ mDefaultProviderIds = in.createStringArrayList();
}
@Override
@@ -198,6 +223,7 @@ public final class RequestInfo implements Parcelable {
dest.writeTypedObject(mCreateCredentialRequest, flags);
dest.writeTypedObject(mGetCredentialRequest, flags);
dest.writeBoolean(mHasPermissionToOverrideDefault);
+ dest.writeStringList(mDefaultProviderIds);
}
@Override
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 79e7574647b3..4a46beb670de 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4677,16 +4677,22 @@ public final class Settings {
"display_color_mode_vendor_hint";
/**
- * Whether or not the peak refresh rate should be forced. 0=no, 1=yes
+ * The user selected min refresh rate in frames per second.
+ *
+ * If this isn't set, 0 will be used.
* @hide
*/
- public static final String FORCE_PEAK_REFRESH_RATE = "force_peak_refresh_rate";
+ @Readable
+ public static final String MIN_REFRESH_RATE = "min_refresh_rate";
/**
- * Whether or not the peak refresh rate should be used for some content. 0=no, 1=yes
+ * The user selected peak refresh rate in frames per second.
+ *
+ * If this isn't set, the system falls back to a device specific default.
* @hide
*/
- public static final String SMOOTH_DISPLAY = "smooth_display";
+ @Readable
+ public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
/**
* The amount of time in milliseconds before the device goes to sleep or begins
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 1a1df6f5f989..751c675b28f4 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -75,12 +75,13 @@ public final class CredentialProviderInfoFactory {
/**
* Constructs an information instance of the credential provider.
*
- * @param context the context object
+ * @param context the context object
* @param serviceComponent the serviceComponent of the provider service
- * @param userId the android userId for which the current process is running
+ * @param userId the android userId for which the current process is running
* @param isSystemProvider whether this provider is a system provider
* @throws PackageManager.NameNotFoundException If provider service is not found
- * @throws SecurityException If provider does not require the relevant permission
+ * @throws SecurityException If provider does not require the relevant
+ * permission
*/
public static CredentialProviderInfo create(
@NonNull Context context,
@@ -99,13 +100,15 @@ public final class CredentialProviderInfoFactory {
/**
* Constructs an information instance of the credential provider.
*
- * @param context the context object
- * @param serviceInfo the service info for the provider app. This must be retrieved from the
- * {@code PackageManager}
- * @param isSystemProvider whether the provider app is a system provider
+ * @param context the context object
+ * @param serviceInfo the service info for the provider app. This must
+ * be retrieved from the
+ * {@code PackageManager}
+ * @param isSystemProvider whether the provider app is a system provider
* @param disableSystemAppVerificationForTests whether to disable system app permission
- * verification so that tests can install system providers
- * @param isEnabled whether the user enabled this provider
+ * verification so that tests can install system
+ * providers
+ * @param isEnabled whether the user enabled this provider
* @throws SecurityException If provider does not require the relevant permission
*/
public static CredentialProviderInfo create(
@@ -374,7 +377,6 @@ public final class CredentialProviderInfoFactory {
if (appInfo == null || serviceInfo == null) {
continue;
}
-
services.add(serviceInfo);
} catch (SecurityException | PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Error getting info for " + serviceInfo, e);
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index 53a5fd5c634d..cf2e6a639fce 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -18,7 +18,6 @@ package android.service.credentials;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import android.Manifest;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
@@ -35,7 +34,7 @@ import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
-import android.util.Log;
+import android.util.Slog;
import java.util.Objects;
@@ -226,7 +225,7 @@ public abstract class CredentialProviderService extends Service {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
}
- Log.d(TAG, "Failed to bind with intent: " + intent);
+ Slog.w(TAG, "Failed to bind with intent: " + intent);
return null;
}
@@ -252,11 +251,6 @@ public abstract class CredentialProviderService extends Service {
GetCredentialException>() {
@Override
public void onResult(BeginGetCredentialResponse result) {
- // If provider service does not possess the HYBRID permission, this
- // check will throw an exception in the provider process.
- if (result.getRemoteCredentialEntry() != null) {
- enforceRemoteEntryPermission();
- }
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -274,15 +268,6 @@ public abstract class CredentialProviderService extends Service {
}
));
}
- private void enforceRemoteEntryPermission() {
- String permission =
- Manifest.permission.PROVIDE_REMOTE_CREDENTIALS;
- getApplicationContext().enforceCallingOrSelfPermission(
- permission,
- String.format("Provider must have %s, in order to set a "
- + "remote entry", permission)
- );
- }
@Override
public void onBeginCreateCredential(BeginCreateCredentialRequest request,
@@ -305,11 +290,6 @@ public abstract class CredentialProviderService extends Service {
BeginCreateCredentialResponse, CreateCredentialException>() {
@Override
public void onResult(BeginCreateCredentialResponse result) {
- // If provider service does not possess the HYBRID permission, this
- // check will throw an exception in the provider process.
- if (result.getRemoteCreateEntry() != null) {
- enforceRemoteEntryPermission();
- }
try {
callback.onSuccess(result);
} catch (RemoteException e) {
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 6061a0f93312..7822ddeb73d8 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -1063,21 +1063,6 @@ public class VoiceInteractionService extends Service {
mActiveVisualQueryDetector = null;
}
mActiveDetectors.remove(detector);
- shutdownHotwordDetectionServiceIfRequiredLocked();
- }
- }
-
- private void shutdownHotwordDetectionServiceIfRequiredLocked() {
- for (HotwordDetector detector : mActiveDetectors) {
- if (detector.isUsingSandboxedDetectionService()) {
- return;
- }
- }
-
- try {
- mSystemService.shutdownHotwordDetectionService();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 8f270f5c5eeb..62f3c909dd4f 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -412,4 +412,32 @@ public class InputConnectionWrapper implements InputConnection {
public boolean setImeConsumesInput(boolean imeConsumesInput) {
return mTarget.setImeConsumesInput(imeConsumesInput);
}
+
+ /**
+ * Called by the system when it needs to take a snapshot of multiple text-related data in an
+ * atomic manner.
+ *
+ * <p><strong>Editor authors</strong>: Supporting this method is strongly encouraged. Atomically
+ * taken {@link TextSnapshot} is going to be really helpful for the system when optimizing IPCs
+ * in a safe and deterministic manner. Return {@code null} if an atomically taken
+ * {@link TextSnapshot} is unavailable. The system continues supporting such a scenario
+ * gracefully.</p>
+ *
+ * <p><strong>IME authors</strong>: Currently IMEs cannot call this method directly and always
+ * receive {@code null} as the result.</p>
+ *
+ * <p>Beware that there is a bug that this method was not overridden in
+ * {@link InputConnectionWrapper}, which ended up always returning {@code null} when gets
+ * called even if the wrapped {@link InputConnection} implements this method. The bug was
+ * fixed in {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}.</p>
+ *
+ * @return {@code null} if {@link TextSnapshot} is unavailable and/or this API is called from
+ * IMEs. Beware the bug in older devices mentioned above.
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Nullable
+ @Override
+ public TextSnapshot takeSnapshot() {
+ return mTarget.takeSnapshot();
+ }
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 51382a4b265f..4d0132e46ba7 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -21,6 +21,8 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -421,36 +423,45 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
return false;
}
- boolean requestsPredictiveBack;
+ boolean requestsPredictiveBack = false;
// Check if the context is from an activity.
while ((context instanceof ContextWrapper) && !(context instanceof Activity)) {
context = ((ContextWrapper) context).getBaseContext();
}
+ boolean shouldCheckActivity = false;
+
if (context instanceof Activity) {
final Activity activity = (Activity) context;
- if (activity.getActivityInfo().hasOnBackInvokedCallbackEnabled()) {
- requestsPredictiveBack =
- activity.getActivityInfo().isOnBackInvokedCallbackEnabled();
+ final ActivityInfo activityInfo = activity.getActivityInfo();
+ if (activityInfo != null) {
+ if (activityInfo.hasOnBackInvokedCallbackEnabled()) {
+ shouldCheckActivity = true;
+ requestsPredictiveBack = activityInfo.isOnBackInvokedCallbackEnabled();
+
+ if (DEBUG) {
+ Log.d(TAG, TextUtils.formatSimple(
+ "Activity: %s isPredictiveBackEnabled=%s",
+ activity.getComponentName(),
+ requestsPredictiveBack));
+ }
+ }
} else {
- requestsPredictiveBack =
- context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
+ Log.w(TAG, "The ActivityInfo is null, so we cannot verify if this Activity"
+ + " has the 'android:enableOnBackInvokedCallback' attribute."
+ + " The application attribute will be used as a fallback.");
}
+ }
- if (DEBUG) {
- Log.d(TAG, TextUtils.formatSimple("Activity: %s isPredictiveBackEnabled=%s",
- activity.getComponentName(),
- requestsPredictiveBack));
- }
- } else {
- requestsPredictiveBack =
- context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
+ if (!shouldCheckActivity) {
+ final ApplicationInfo applicationInfo = context.getApplicationInfo();
+ requestsPredictiveBack = applicationInfo.isOnBackInvokedCallbackEnabled();
if (DEBUG) {
Log.d(TAG, TextUtils.formatSimple("App: %s requestsPredictiveBack=%s",
- context.getApplicationInfo().packageName,
+ applicationInfo.packageName,
requestsPredictiveBack));
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 499d38c31b59..50f393b53277 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1725,16 +1725,17 @@ public class ResolverActivity extends Activity implements
if (inWorkProfile) {
((TextView) findViewById(R.id.open_cross_profile)).setText(
- devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK,
- () -> getString(R.string.miniresolver_open_in_work, targetDisplayLabel),
+ devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL,
+ () -> getString(R.string.miniresolver_open_in_personal,
+ targetDisplayLabel),
targetDisplayLabel));
((Button) findViewById(R.id.use_same_profile_browser)).setText(
devicePolicyResourcesManager.getString(MINIRESOLVER_USE_WORK_BROWSER,
() -> getString(R.string.miniresolver_use_work_browser)));
} else {
((TextView) findViewById(R.id.open_cross_profile)).setText(
- devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL,
- () -> getString(R.string.miniresolver_open_in_personal,
+ devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK,
+ () -> getString(R.string.miniresolver_open_in_work,
targetDisplayLabel),
targetDisplayLabel));
((Button) findViewById(R.id.use_same_profile_browser)).setText(
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
deleted file mode 100644
index 39d8380c7e95..000000000000
--- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.display;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.Display;
-
-/**
- * Constants and utility methods for refresh rate settings.
- */
-public class RefreshRateSettingsUtils {
-
- private static final String TAG = "RefreshRateSettingsUtils";
-
- public static final float DEFAULT_REFRESH_RATE = 60f;
-
- /**
- * Find the highest refresh rate among all the modes of the default display.
- * @param context The context
- * @return The highest refresh rate
- */
- public static float findHighestRefreshRateForDefaultDisplay(Context context) {
- final DisplayManager dm = context.getSystemService(DisplayManager.class);
- final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
-
- if (display == null) {
- Log.w(TAG, "No valid default display device");
- return DEFAULT_REFRESH_RATE;
- }
-
- float maxRefreshRate = DEFAULT_REFRESH_RATE;
- for (Display.Mode mode : display.getSupportedModes()) {
- if (Math.round(mode.getRefreshRate()) > maxRefreshRate) {
- maxRefreshRate = mode.getRefreshRate();
- }
- }
- return maxRefreshRate;
- }
-
- /**
- * Get the min refresh rate which is determined by
- * {@link Settings.System.FORCE_PEAK_REFRESH_RATE}.
- * @param context The context
- * @return The min refresh rate
- */
- public static float getMinRefreshRate(Context context) {
- final ContentResolver cr = context.getContentResolver();
- int forcePeakRefreshRateSetting = Settings.System.getIntForUser(cr,
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1, cr.getUserId());
- return forcePeakRefreshRateSetting == 1
- ? findHighestRefreshRateForDefaultDisplay(context)
- : 0;
- }
-
- /**
- * Get the peak refresh rate which is determined by {@link Settings.System.SMOOTH_DISPLAY}.
- * @param context The context
- * @param defaultPeakRefreshRate The refresh rate to return if the setting doesn't have a value
- * @return The peak refresh rate
- */
- public static float getPeakRefreshRate(Context context, float defaultPeakRefreshRate) {
- final ContentResolver cr = context.getContentResolver();
- int smoothDisplaySetting = Settings.System.getIntForUser(cr,
- Settings.System.SMOOTH_DISPLAY, -1, cr.getUserId());
- switch (smoothDisplaySetting) {
- case 0:
- return DEFAULT_REFRESH_RATE;
- case 1:
- return findHighestRefreshRateForDefaultDisplay(context);
- default:
- return defaultPeakRefreshRate;
- }
- }
-}
diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto
index 9ccadbf6eb2d..3a9e5eeb4877 100644
--- a/core/proto/android/companion/telecom.proto
+++ b/core/proto/android/companion/telecom.proto
@@ -20,9 +20,9 @@ package android.companion;
option java_multiple_files = true;
-// Next index: 2
+// Next index: 4
message Telecom {
- // Next index: 5
+ // Next index: 6
message Call {
// UUID representing this call
int64 id = 1;
@@ -34,6 +34,8 @@ message Telecom {
// Human-readable name of the app processing this call
string app_name = 2;
bytes app_icon = 3;
+ // Unique identifier for this app, such as a package name.
+ string app_identifier = 4;
}
Origin origin = 2;
@@ -59,9 +61,11 @@ message Telecom {
REJECT_AND_BLOCK = 9;
IGNORE = 10;
}
- repeated Control controls_available = 4;
+ repeated Control controls = 4;
}
// The list of active calls.
repeated Call calls = 1;
+ // The list of requested calls or call changes.
+ repeated Call requests = 2;
}
diff --git a/core/res/res/values-mcc310/config.xml b/core/res/res/values-mcc310/config.xml
index df398f9aab32..76abceeb74b1 100644
--- a/core/res/res/values-mcc310/config.xml
+++ b/core/res/res/values-mcc310/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_mcc_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc311/config.xml b/core/res/res/values-mcc311/config.xml
index df398f9aab32..6e0b678f94d9 100644
--- a/core/res/res/values-mcc311/config.xml
+++ b/core/res/res/values-mcc311/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc312/config.xml b/core/res/res/values-mcc312/config.xml
index df398f9aab32..6e0b678f94d9 100644
--- a/core/res/res/values-mcc312/config.xml
+++ b/core/res/res/values-mcc312/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc313/config.xml b/core/res/res/values-mcc313/config.xml
index df398f9aab32..6e0b678f94d9 100644
--- a/core/res/res/values-mcc313/config.xml
+++ b/core/res/res/values-mcc313/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc314/config.xml b/core/res/res/values-mcc314/config.xml
index df398f9aab32..6e0b678f94d9 100644
--- a/core/res/res/values-mcc314/config.xml
+++ b/core/res/res/values-mcc314/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc315/config.xml b/core/res/res/values-mcc315/config.xml
index df398f9aab32..6e0b678f94d9 100644
--- a/core/res/res/values-mcc315/config.xml
+++ b/core/res/res/values-mcc315/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc316/config.xml b/core/res/res/values-mcc316/config.xml
index df398f9aab32..6e0b678f94d9 100644
--- a/core/res/res/values-mcc316/config.xml
+++ b/core/res/res/values-mcc316/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 12dad7eb1faa..7bc3ab80da99 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2105,9 +2105,6 @@
<!-- The default volume for the ring stream -->
<integer name="config_audio_ring_vol_default">5</integer>
- <!-- Enable sound dose computation and warnings -->
- <bool name="config_audio_csd_enabled_default">true</bool>
-
<!-- The default value for whether head tracking for
spatial audio is enabled for a newly connected audio device -->
<bool name="config_spatial_audio_head_tracking_enabled_default">false</bool>
@@ -2939,6 +2936,9 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">true</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">true</bool>
+
<!-- Whether safe headphone volume warning dialog is disabled on Vol+ (operator specific). -->
<bool name="config_safe_media_disable_on_volume_up">true</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index bc0af12e9569..ad864b13b16d 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -96,12 +96,12 @@
<!-- Width of the navigation bar when it is placed vertically on the screen in car mode -->
<dimen name="navigation_bar_width_car_mode">96dp</dimen>
<!-- Height of notification icons in the status bar -->
- <dimen name="status_bar_icon_size">22dip</dimen>
+ <dimen name="status_bar_icon_size">18sp</dimen>
<!-- Desired size of system icons in status bar. -->
- <dimen name="status_bar_system_icon_size">15dp</dimen>
+ <dimen name="status_bar_system_icon_size">15sp</dimen>
<!-- Intrinsic size of most system icons in status bar. This is the default value that
is used if a Drawable reports an intrinsic size of 0. -->
- <dimen name="status_bar_system_icon_intrinsic_size">17dp</dimen>
+ <dimen name="status_bar_system_icon_intrinsic_size">17sp</dimen>
<!-- Size of the giant number (unread count) in the notifications -->
<dimen name="status_bar_content_number_size">48sp</dimen>
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
@@ -330,7 +330,7 @@
<dimen name="notification_icon_circle_start">16dp</dimen>
<!-- size (width and height) of the icon in the notification header -->
- <dimen name="notification_header_icon_size_ambient">18dp</dimen>
+ <dimen name="notification_header_icon_size_ambient">18sp</dimen>
<!-- The margin before the start of the app name in the header. -->
<dimen name="notification_header_app_name_margin_start">3dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bb10f7ae7c93..e25425d6cb18 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -283,7 +283,6 @@
<java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/>
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
- <java-symbol type="bool" name="config_audio_csd_enabled_default" />
<java-symbol type="integer" name="config_audio_notif_vol_default" />
<java-symbol type="integer" name="config_audio_notif_vol_steps" />
<java-symbol type="integer" name="config_audio_ring_vol_default" />
@@ -349,6 +348,7 @@
<java-symbol type="bool" name="config_useDevInputEventForAudioJack" />
<java-symbol type="bool" name="config_safe_media_volume_enabled" />
<java-symbol type="bool" name="config_safe_media_disable_on_volume_up" />
+ <java-symbol type="bool" name="config_safe_sound_dosage_enabled" />
<java-symbol type="bool" name="config_camera_sound_forced" />
<java-symbol type="bool" name="config_dontPreferApn" />
<java-symbol type="bool" name="config_restartRadioAfterProvisioning" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 596f3515dda5..7c2759af156f 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1183,6 +1183,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "-1005167552": {
+ "message": "Playing #%d in parallel on track #%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"-1003678883": {
"message": "Cleaning splash screen token=%s",
"level": "VERBOSE",
@@ -1447,6 +1453,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-774908272": {
+ "message": "Marking #%d animation as SYNC.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"-771177730": {
"message": "Removing focused app token:%s displayId=%d",
"level": "VERBOSE",
@@ -1495,12 +1507,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-741766551": {
- "message": "Content Recording: Ignoring session on invalid virtual display",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecordingController.java"
- },
"-732715767": {
"message": "Unable to retrieve window container to start recording for display %d",
"level": "VERBOSE",
@@ -2017,6 +2023,12 @@
"group": "WM_DEBUG_WALLPAPER",
"at": "com\/android\/server\/wm\/WallpaperController.java"
},
+ "-266707683": {
+ "message": "Moving #%d from collecting to waiting.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"-262984451": {
"message": "Relaunch failed %s",
"level": "INFO",
@@ -2089,6 +2101,12 @@
"group": "WM_DEBUG_WINDOW_MOVEMENT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-186693085": {
+ "message": "Starting a Recents transition which can be parallel.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-182877285": {
"message": "Wallpaper layer changed: assigning layers + relayout",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index d3f395846894..24fd86b45df4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -297,7 +297,7 @@ public class Bubble implements BubbleViewProvider {
public BubbleInfo asBubbleBarBubble() {
return new BubbleInfo(getKey(),
getFlags(),
- getShortcutInfo().getId(),
+ getShortcutId(),
getIcon(),
getUser().getIdentifier(),
getPackageName());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
index 0f9260c9deaa..9abf0f678179 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
@@ -38,7 +38,7 @@ public interface TaskStackListenerCallback {
default void onTaskStackChanged() { }
- default void onTaskProfileLocked(RunningTaskInfo taskInfo) { }
+ default void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { }
default void onTaskDisplayChanged(int taskId, int newDisplayId) { }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index e2106e478bb3..d8859bac471f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -150,8 +150,8 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler.
}
@Override
- public void onTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo) {
- mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskInfo).sendToTarget();
+ public void onTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo, int userId) {
+ mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget();
}
@Override
@@ -348,8 +348,9 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler.
case ON_TASK_PROFILE_LOCKED: {
final ActivityManager.RunningTaskInfo
info = (ActivityManager.RunningTaskInfo) msg.obj;
+ final int userId = msg.arg1;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskProfileLocked(info);
+ mTaskStackListeners.get(i).onTaskProfileLocked(info, userId);
}
break;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
index b0dea7231a1e..d27d05b207a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
@@ -35,6 +35,7 @@ public class BubbleInfo implements Parcelable {
private String mKey; // Same key as the Notification
private int mFlags; // Flags from BubbleMetadata
+ @Nullable
private String mShortcutId;
private int mUserId;
private String mPackageName;
@@ -46,7 +47,7 @@ public class BubbleInfo implements Parcelable {
@Nullable
private Icon mIcon;
- public BubbleInfo(String key, int flags, String shortcutId, @Nullable Icon icon,
+ public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
int userId, String packageName) {
mKey = key;
mFlags = flags;
@@ -69,10 +70,12 @@ public class BubbleInfo implements Parcelable {
return mKey;
}
+ @Nullable
public String getShortcutId() {
return mShortcutId;
}
+ @Nullable
public Icon getIcon() {
return mIcon;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 86ea72582a52..b9d2be280efb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -264,10 +264,10 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
/**
* Show apps on desktop
*/
- void showDesktopApps() {
+ void showDesktopApps(int displayId) {
// Bring apps to front, ignoring their visibility status to always ensure they are on top.
WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(wct);
+ bringDesktopAppsToFront(displayId, wct);
if (!wct.isEmpty()) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -280,12 +280,12 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
}
/** Get number of tasks that are marked as visible */
- int getVisibleTaskCount() {
- return mDesktopModeTaskRepository.getVisibleTaskCount();
+ int getVisibleTaskCount(int displayId) {
+ return mDesktopModeTaskRepository.getVisibleTaskCount(displayId);
}
- private void bringDesktopAppsToFront(WindowContainerTransaction wct) {
- final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks();
+ private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) {
+ final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId);
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
final List<RunningTaskInfo> taskInfos = new ArrayList<>();
@@ -386,6 +386,7 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
+ RunningTaskInfo triggerTask = request.getTriggerTask();
// Only do anything if we are in desktop mode and opening/moving-to-front a task/app in
// freeform
if (!DesktopModeStatus.isActive(mContext)) {
@@ -399,16 +400,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
WindowManager.transitTypeToString(request.getType()));
return null;
}
- if (request.getTriggerTask() == null
- || request.getTriggerTask().getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task");
return null;
}
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request);
WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(wct);
- wct.reorder(request.getTriggerTask().token, true /* onTop */);
+ bringDesktopAppsToFront(triggerTask.displayId, wct);
+ wct.reorder(triggerTask.token, true /* onTop */);
return wct;
}
@@ -493,16 +493,17 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
mController = null;
}
- public void showDesktopApps() {
+ @Override
+ public void showDesktopApps(int displayId) {
executeRemoteCallWithTaskPermission(mController, "showDesktopApps",
- DesktopModeController::showDesktopApps);
+ controller -> controller.showDesktopApps(displayId));
}
@Override
- public int getVisibleTaskCount() throws RemoteException {
+ public int getVisibleTaskCount(int displayId) throws RemoteException {
int[] result = new int[1];
executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount",
- controller -> result[0] = controller.getVisibleTaskCount(),
+ controller -> result[0] = controller.getVisibleTaskCount(displayId),
true /* blocking */
);
return result[0];
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 12f8ea23ac8f..00cc57f0b99c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -20,6 +20,8 @@ import android.graphics.Region
import android.util.ArrayMap
import android.util.ArraySet
import android.util.SparseArray
+import androidx.core.util.forEach
+import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -29,14 +31,18 @@ import java.util.function.Consumer
*/
class DesktopModeTaskRepository {
- /**
- * Set of task ids that are marked as active in desktop mode.
- * Active tasks in desktop mode are freeform tasks that are visible or have been visible after
- * desktop mode was activated.
- * Task gets removed from this list when it vanishes. Or when desktop mode is turned off.
- */
- private val activeTasks = ArraySet<Int>()
- private val visibleTasks = ArraySet<Int>()
+ /** Task data that is tracked per display */
+ private data class DisplayData(
+ /**
+ * Set of task ids that are marked as active in desktop mode. Active tasks in desktop mode
+ * are freeform tasks that are visible or have been visible after desktop mode was
+ * activated. Task gets removed from this list when it vanishes. Or when desktop mode is
+ * turned off.
+ */
+ val activeTasks: ArraySet<Int> = ArraySet(),
+ val visibleTasks: ArraySet<Int> = ArraySet(),
+ )
+
// Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
private val freeformTasksInZOrder = mutableListOf<Int>()
private val activeTasksListeners = ArraySet<ActiveTasksListener>()
@@ -47,9 +53,22 @@ class DesktopModeTaskRepository {
private var desktopGestureExclusionListener: Consumer<Region>? = null
private var desktopGestureExclusionExecutor: Executor? = null
- /**
- * Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository.
- */
+ private val displayData =
+ object : SparseArray<DisplayData>() {
+ /**
+ * Get the [DisplayData] associated with this [displayId]
+ *
+ * Creates a new instance if one does not exist
+ */
+ fun getOrCreate(displayId: Int): DisplayData {
+ if (!contains(displayId)) {
+ put(displayId, DisplayData())
+ }
+ return get(displayId)
+ }
+ }
+
+ /** Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. */
fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) {
activeTasksListeners.add(activeTasksListener)
}
@@ -57,10 +76,17 @@ class DesktopModeTaskRepository {
/**
* Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not.
*/
- fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) {
- visibleTasksListeners.put(visibleTasksListener, executor)
- executor.execute(
- Runnable { visibleTasksListener.onVisibilityChanged(visibleTasks.size > 0) })
+ fun addVisibleTasksListener(
+ visibleTasksListener: VisibleTasksListener,
+ executor: Executor
+ ) {
+ visibleTasksListeners[visibleTasksListener] = executor
+ displayData.keyIterator().forEach { displayId ->
+ val visibleTasks = getVisibleTaskCount(displayId)
+ executor.execute {
+ visibleTasksListener.onVisibilityChanged(displayId, visibleTasks > 0)
+ }
+ }
}
/**
@@ -100,14 +126,21 @@ class DesktopModeTaskRepository {
}
/**
- * Mark a task with given [taskId] as active.
+ * Mark a task with given [taskId] as active on given [displayId]
*
- * @return `true` if the task was not active
+ * @return `true` if the task was not active on given [displayId]
*/
- fun addActiveTask(taskId: Int): Boolean {
- val added = activeTasks.add(taskId)
+ fun addActiveTask(displayId: Int, taskId: Int): Boolean {
+ // Check if task is active on another display, if so, remove it
+ displayData.forEach { id, data ->
+ if (id != displayId && data.activeTasks.remove(taskId)) {
+ activeTasksListeners.onEach { it.onActiveTasksChanged(id) }
+ }
+ }
+
+ val added = displayData.getOrCreate(displayId).activeTasks.add(taskId)
if (added) {
- activeTasksListeners.onEach { it.onActiveTasksChanged() }
+ activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
}
return added
}
@@ -118,65 +151,93 @@ class DesktopModeTaskRepository {
* @return `true` if the task was active
*/
fun removeActiveTask(taskId: Int): Boolean {
- val removed = activeTasks.remove(taskId)
- if (removed) {
- activeTasksListeners.onEach { it.onActiveTasksChanged() }
+ var result = false
+ displayData.forEach { displayId, data ->
+ if (data.activeTasks.remove(taskId)) {
+ activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
+ result = true
+ }
}
- return removed
+ return result
}
/**
* Check if a task with the given [taskId] was marked as an active task
*/
fun isActiveTask(taskId: Int): Boolean {
- return activeTasks.contains(taskId)
+ return displayData.valueIterator().asSequence().any { data ->
+ data.activeTasks.contains(taskId)
+ }
}
/**
* Whether a task is visible.
*/
fun isVisibleTask(taskId: Int): Boolean {
- return visibleTasks.contains(taskId)
+ return displayData.valueIterator().asSequence().any { data ->
+ data.visibleTasks.contains(taskId)
+ }
}
/**
- * Get a set of the active tasks
+ * Get a set of the active tasks for given [displayId]
*/
- fun getActiveTasks(): ArraySet<Int> {
- return ArraySet(activeTasks)
+ fun getActiveTasks(displayId: Int): ArraySet<Int> {
+ return ArraySet(displayData[displayId]?.activeTasks)
}
/**
* Get a list of freeform tasks, ordered from top-bottom (top at index 0).
*/
+ // TODO(b/278084491): pass in display id
fun getFreeformTasksInZOrder(): List<Int> {
return freeformTasksInZOrder
}
/**
* Updates whether a freeform task with this id is visible or not and notifies listeners.
+ *
+ * If the task was visible on a different display with a different displayId, it is removed from
+ * the set of visible tasks on that display. Listeners will be notified.
*/
- fun updateVisibleFreeformTasks(taskId: Int, visible: Boolean) {
- val prevCount: Int = visibleTasks.size
+ fun updateVisibleFreeformTasks(displayId: Int, taskId: Int, visible: Boolean) {
if (visible) {
- visibleTasks.add(taskId)
+ // Task is visible. Check if we need to remove it from any other display.
+ val otherDisplays = displayData.keyIterator().asSequence().filter { it != displayId }
+ for (otherDisplayId in otherDisplays) {
+ if (displayData[otherDisplayId].visibleTasks.remove(taskId)) {
+ // Task removed from other display, check if we should notify listeners
+ if (displayData[otherDisplayId].visibleTasks.isEmpty()) {
+ notifyVisibleTaskListeners(otherDisplayId, hasVisibleFreeformTasks = false)
+ }
+ }
+ }
+ }
+
+ val prevCount = getVisibleTaskCount(displayId)
+ if (visible) {
+ displayData.getOrCreate(displayId).visibleTasks.add(taskId)
} else {
- visibleTasks.remove(taskId)
+ displayData[displayId]?.visibleTasks?.remove(taskId)
}
- if (prevCount == 0 && visibleTasks.size == 1 ||
- prevCount > 0 && visibleTasks.size == 0) {
- for ((listener, executor) in visibleTasksListeners) {
- executor.execute(
- Runnable { listener.onVisibilityChanged(visibleTasks.size > 0) })
- }
+ val newCount = getVisibleTaskCount(displayId)
+ // Check if count changed and if there was no tasks or this is the first task
+ if (prevCount != newCount && (prevCount == 0 || newCount == 0)) {
+ notifyVisibleTaskListeners(displayId, newCount > 0)
+ }
+ }
+
+ private fun notifyVisibleTaskListeners(displayId: Int, hasVisibleFreeformTasks: Boolean) {
+ visibleTasksListeners.forEach { (listener, executor) ->
+ executor.execute { listener.onVisibilityChanged(displayId, hasVisibleFreeformTasks) }
}
}
/**
- * Get number of tasks that are marked as visible
+ * Get number of tasks that are marked as visible on given [displayId]
*/
- fun getVisibleTaskCount(): Int {
- return visibleTasks.size
+ fun getVisibleTaskCount(displayId: Int): Int {
+ return displayData[displayId]?.visibleTasks?.size ?: 0
}
/**
@@ -226,7 +287,7 @@ class DesktopModeTaskRepository {
* Called when the active tasks change in desktop mode.
*/
@JvmDefault
- fun onActiveTasksChanged() {}
+ fun onActiveTasksChanged(displayId: Int) {}
}
/**
@@ -237,6 +298,6 @@ class DesktopModeTaskRepository {
* Called when the desktop starts or stops showing freeform tasks.
*/
@JvmDefault
- fun onVisibilityChanged(hasVisibleFreeformTasks: Boolean) {}
+ fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {}
}
}
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 0d5602365578..c814fe575e81 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
@@ -97,10 +97,11 @@ class DesktopTasksController(
}
/** Show all tasks, that are part of the desktop, on top of launcher */
- fun showDesktopApps() {
+ fun showDesktopApps(displayId: Int) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps")
val wct = WindowContainerTransaction()
- bringDesktopAppsToFront(wct)
+ // TODO(b/278084491): pass in display id
+ bringDesktopAppsToFront(displayId, wct)
// Execute transaction if there are pending operations
if (!wct.isEmpty) {
@@ -114,8 +115,8 @@ class DesktopTasksController(
}
/** Get number of tasks that are marked as visible */
- fun getVisibleTaskCount(): Int {
- return desktopModeTaskRepository.getVisibleTaskCount()
+ fun getVisibleTaskCount(displayId: Int): Int {
+ return desktopModeTaskRepository.getVisibleTaskCount(displayId)
}
/** Move a task with given `taskId` to desktop */
@@ -129,7 +130,7 @@ class DesktopTasksController(
val wct = WindowContainerTransaction()
// Bring other apps to front first
- bringDesktopAppsToFront(wct)
+ bringDesktopAppsToFront(task.displayId, wct)
addMoveToDesktopChanges(wct, task.token)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
@@ -165,7 +166,7 @@ class DesktopTasksController(
freeformBounds: Rect
) {
val wct = WindowContainerTransaction()
- bringDesktopAppsToFront(wct)
+ bringDesktopAppsToFront(taskInfo.displayId, wct)
addMoveToDesktopChanges(wct, taskInfo.getToken())
wct.setBounds(taskInfo.token, freeformBounds)
@@ -244,9 +245,9 @@ class DesktopTasksController(
?: WINDOWING_MODE_UNDEFINED
}
- private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) {
+ private fun bringDesktopAppsToFront(displayId: Int, wct: WindowContainerTransaction) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront")
- val activeTasks = desktopModeTaskRepository.getActiveTasks()
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId)
// First move home to front and then other tasks on top of it
moveHomeTaskToFront(wct)
@@ -290,18 +291,17 @@ class DesktopTasksController(
request: TransitionRequestInfo
): WindowContainerTransaction? {
// Check if we should skip handling this transition
- val task: RunningTaskInfo? = request.triggerTask
val shouldHandleRequest =
when {
// Only handle open or to front transitions
request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false
// Only handle when it is a task transition
- task == null -> false
+ request.triggerTask == null -> false
// Only handle standard type tasks
- task.activityType != ACTIVITY_TYPE_STANDARD -> false
+ request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false
// Only handle fullscreen or freeform tasks
- task.windowingMode != WINDOWING_MODE_FULLSCREEN &&
- task.windowingMode != WINDOWING_MODE_FREEFORM -> false
+ request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN &&
+ request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false
// Otherwise process it
else -> true
}
@@ -310,10 +310,11 @@ class DesktopTasksController(
return null
}
- val activeTasks = desktopModeTaskRepository.getActiveTasks()
+ val task: RunningTaskInfo = request.triggerTask
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
// Check if we should switch a fullscreen task to freeform
- if (task?.windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) {
// If there are any visible desktop tasks, switch the task to freeform
if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
ProtoLog.d(
@@ -329,7 +330,7 @@ class DesktopTasksController(
}
// CHeck if we should switch a freeform task to fullscreen
- if (task?.windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (task.windowingMode == WINDOWING_MODE_FREEFORM) {
// If no visible desktop tasks, switch this task to freeform as the transition came
// outside of this controller
if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
@@ -559,20 +560,19 @@ class DesktopTasksController(
controller = null
}
- override fun showDesktopApps() {
+ override fun showDesktopApps(displayId: Int) {
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
- "showDesktopApps",
- Consumer(DesktopTasksController::showDesktopApps)
- )
+ "showDesktopApps"
+ ) { c -> c.showDesktopApps(displayId) }
}
- override fun getVisibleTaskCount(): Int {
+ override fun getVisibleTaskCount(displayId: Int): Int {
val result = IntArray(1)
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
"getVisibleTaskCount",
- { controller -> result[0] = controller.getVisibleTaskCount() },
+ { controller -> result[0] = controller.getVisibleTaskCount(displayId) },
true /* blocking */
)
return result[0]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index d0739e14675f..899d67267e69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -21,9 +21,9 @@ package com.android.wm.shell.desktopmode;
*/
interface IDesktopMode {
- /** Show apps on the desktop */
- void showDesktopApps();
+ /** Show apps on the desktop on the given display */
+ void showDesktopApps(int displayId);
- /** Get count of visible desktop tasks */
- int getVisibleTaskCount();
+ /** Get count of visible desktop tasks on the given display */
+ int getVisibleTaskCount(int displayId);
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 48487bc4a3d6..22541bbd892a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -94,11 +94,12 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
if (taskInfo.isVisible) {
- if (repository.addActiveTask(taskInfo.taskId)) {
+ if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
}
- repository.updateVisibleFreeformTasks(taskInfo.taskId, true);
+ repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId,
+ true);
}
});
}
@@ -117,7 +118,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Removing active freeform task: #%d", taskInfo.taskId);
}
- repository.updateVisibleFreeformTasks(taskInfo.taskId, false);
+ repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, false);
});
}
@@ -137,12 +138,13 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
if (DesktopModeStatus.isAnyEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
if (taskInfo.isVisible) {
- if (repository.addActiveTask(taskInfo.taskId)) {
+ if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
}
}
- repository.updateVisibleFreeformTasks(taskInfo.taskId, taskInfo.isVisible);
+ repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId,
+ taskInfo.isVisible);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 566c130c7573..d3e7f9ca4227 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -72,7 +72,6 @@ import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import android.window.WindowContainerTransactionCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -139,17 +138,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// the runnable to execute after WindowContainerTransactions is applied to finish resizing pip
private Runnable mPipFinishResizeWCTRunnable;
- private final WindowContainerTransactionCallback mPipFinishResizeWCTCallback =
- new WindowContainerTransactionCallback() {
- @Override
- public void onTransactionReady(int id, SurfaceControl.Transaction t) {
- t.apply();
-
- // execute the runnable if non-null after WCT is applied to finish resizing pip
- maybePerformFinishResizeCallback();
- }
- };
-
private void maybePerformFinishResizeCallback() {
if (mPipFinishResizeWCTRunnable != null) {
mPipFinishResizeWCTRunnable.run();
@@ -175,6 +163,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final int direction = animator.getTransitionDirection();
if (mIsCancelled) {
sendOnPipTransitionFinished(direction);
+ maybePerformFinishResizeCallback();
return;
}
final int animationType = animator.getAnimationType();
@@ -199,6 +188,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
|| isRemovePipDirection(direction);
if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
|| isExitPipDirection) {
+ // execute the finish resize callback if needed after the transaction is committed
+ tx.addTransactionCommittedListener(mMainExecutor,
+ PipTaskOrganizer.this::maybePerformFinishResizeCallback);
+
// Finish resize as long as we're not exiting PIP, or, if we are, only if this is
// the end of an exit PIP animation.
// This is necessary in case there was a resize animation ongoing when exit PIP
@@ -1614,12 +1607,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
mSplitScreenOptional.ifPresent(splitScreenController ->
splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
- } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
- // when leaving PiP we can call the callback without sync
- maybePerformFinishResizeCallback();
- mTaskOrganizer.applyTransaction(wct);
} else {
- mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback);
+ mTaskOrganizer.applyTransaction(wct);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index b0bb14b49db6..f8e143575583 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1060,13 +1060,22 @@ public class PipController implements PipTransitionController.PipTransitionCallb
/** Save the state to restore to on re-entry. */
public void saveReentryState(Rect pipBounds) {
float snapFraction = mPipBoundsAlgorithm.getSnapFraction(pipBounds);
- if (mPipBoundsState.hasUserResizedPip()) {
- final Rect reentryBounds = mTouchHandler.getUserResizeBounds();
- final Size reentrySize = new Size(reentryBounds.width(), reentryBounds.height());
- mPipBoundsState.saveReentryState(reentrySize, snapFraction);
- } else {
+
+ if (!mPipBoundsState.hasUserResizedPip()) {
mPipBoundsState.saveReentryState(null /* bounds */, snapFraction);
+ return;
}
+
+ Size reentrySize = new Size(pipBounds.width(), pipBounds.height());
+
+ // TODO: b/279937014 Investigate why userResizeBounds are empty with shell transitions on
+ // fallback to using the userResizeBounds if userResizeBounds are not empty
+ if (!mTouchHandler.getUserResizeBounds().isEmpty()) {
+ Rect userResizeBounds = mTouchHandler.getUserResizeBounds();
+ reentrySize = new Size(userResizeBounds.width(), userResizeBounds.height());
+ }
+
+ mPipBoundsState.saveReentryState(reentrySize, snapFraction);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 956af709f156..281cae5e4ffa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -104,6 +104,7 @@ public class PipResizeGestureHandler {
private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
+ private boolean mEnableTouch;
private boolean mEnablePinchResize;
private boolean mEnableDragCornerResize;
private boolean mIsSysUiStateValid;
@@ -138,6 +139,7 @@ public class PipResizeGestureHandler {
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
+ mEnableTouch = true;
mUpdateResizeBoundsCallback = (rect) -> {
mUserResizeBounds.set(rect);
@@ -248,6 +250,11 @@ public class PipResizeGestureHandler {
return;
}
+ if (!mEnableTouch) {
+ // No need to handle anything if touches are not enabled for resizing.
+ return;
+ }
+
// Don't allow resize when PiP is stashed.
if (mPipBoundsState.isStashed()) {
return;
@@ -581,14 +588,13 @@ public class PipResizeGestureHandler {
mLastResizeBounds, movementBounds);
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
- // disable the pinch resizing until the final bounds are updated
- final boolean prevEnablePinchResize = mEnablePinchResize;
- mEnablePinchResize = false;
+ // disable the resizing until the final bounds are updated
+ mEnableTouch = false;
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
// reset the pinch resizing to its default state
- mEnablePinchResize = prevEnablePinchResize;
+ mEnableTouch = true;
});
} else {
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index c5bfd8753994..5c9709c756f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -245,7 +245,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@Override
- public void onActiveTasksChanged() {
+ public void onActiveTasksChanged(int displayId) {
notifyRecentTasksChanged();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 7991c529aa49..2ab4c751d399 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -82,6 +82,10 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
mGuard.open("release");
}
+ SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
/**
* Sets the provided {@link TaskViewBase}, which is used to notify the client part about the
* task related changes and getting the current bounds.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 81d69a4fa611..c7e534a2bcf5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -336,6 +336,19 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
tv.prepareOpenAnimation(taskIsNew, startTransaction, finishTransaction,
chg.getTaskInfo(), chg.getLeash(), wct);
changesHandled++;
+ } else if (chg.getMode() == TRANSIT_CHANGE) {
+ TaskViewTaskController tv = findTaskView(chg.getTaskInfo());
+ if (tv == null) {
+ if (pending != null) {
+ Slog.w(TAG, "Found a non-TaskView task in a TaskView Transition. This "
+ + "shouldn't happen, so there may be a visual artifact: "
+ + chg.getTaskInfo().taskId);
+ }
+ continue;
+ }
+ startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
+ finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
+ changesHandled++;
}
}
if (stillNeedsMatchingLaunch) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java
index 9d7c39f1c90e..ba364f8a6e59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java
@@ -73,7 +73,8 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
if (mHandlerIds.containsKey(handler)) {
handlerId = mHandlerIds.get(handler);
} else {
- handlerId = mHandlerIds.size();
+ // + 1 to avoid 0 ids which can be confused with missing value when dumped to proto
+ handlerId = mHandlerIds.size() + 1;
mHandlerIds.put(handler, handlerId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index ef8332f5e347..1d416c65851b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -86,9 +86,10 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback {
public void onDragPositioningEnd(float x, float y) {
PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y,
mRepositionStartPoint);
- if (mHasMoved && DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved,
- mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
- mDisplayController, mDesktopWindowDecoration)) {
+ if (mHasMoved) {
+ DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved,
+ mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
+ mDisplayController, mDesktopWindowDecoration);
DragPositioningCallbackUtility.applyTaskBoundsChange(
new WindowContainerTransaction(), mDesktopWindowDecoration,
mRepositionTaskBounds, mTaskOrganizer);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index de967bfa288b..afec1ee12341 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -33,6 +33,7 @@ import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.os.Bundle;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,6 +43,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.bubbles.BubbleInfo;
import org.junit.Before;
import org.junit.Test;
@@ -77,7 +79,7 @@ public class BubbleTest extends ShellTestCase {
Intent target = new Intent(mContext, BubblesTestActivity.class);
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0, target, PendingIntent.FLAG_MUTABLE),
- Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
+ Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
.build();
when(mSbn.getNotification()).thenReturn(mNotif);
when(mNotif.getBubbleMetadata()).thenReturn(metadata);
@@ -179,6 +181,34 @@ public class BubbleTest extends ShellTestCase {
assertThat(bubble.isConversation()).isFalse();
}
+ @Test
+ public void testBubbleAsBubbleBarBubble_withShortcut() {
+ Bubble bubble = createBubbleWithShortcut();
+ BubbleInfo bubbleInfo = bubble.asBubbleBarBubble();
+
+ assertThat(bubble.getShortcutInfo()).isNotNull();
+ assertThat(bubbleInfo.getShortcutId()).isNotNull();
+ assertThat(bubbleInfo.getShortcutId()).isEqualTo(bubble.getShortcutId());
+ assertThat(bubbleInfo.getKey()).isEqualTo(bubble.getKey());
+ assertThat(bubbleInfo.getUserId()).isEqualTo(bubble.getUser().getIdentifier());
+ assertThat(bubbleInfo.getPackageName()).isEqualTo(bubble.getPackageName());
+ }
+
+ @Test
+ public void testBubbleAsBubbleBarBubble_withoutShortcut() {
+ Intent intent = new Intent(mContext, BubblesTestActivity.class);
+ intent.setPackage(mContext.getPackageName());
+ Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1 /* userId */),
+ null /* icon */, mMainExecutor);
+ BubbleInfo bubbleInfo = bubble.asBubbleBarBubble();
+
+ assertThat(bubble.getShortcutInfo()).isNull();
+ assertThat(bubbleInfo.getShortcutId()).isNull();
+ assertThat(bubbleInfo.getKey()).isEqualTo(bubble.getKey());
+ assertThat(bubbleInfo.getUserId()).isEqualTo(bubble.getUser().getIdentifier());
+ assertThat(bubbleInfo.getPackageName()).isEqualTo(bubble.getPackageName());
+ }
+
private Bubble createBubbleWithShortcut() {
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext)
.setId("mockShortcutId")
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
index 1347e061eb45..60ee918ee545 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
@@ -112,9 +112,9 @@ public class TaskStackListenerImplTest extends ShellTestCase {
@Test
public void testOnTaskProfileLocked() {
ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class);
- mImpl.onTaskProfileLocked(info);
- verify(mCallback).onTaskProfileLocked(eq(info));
- verify(mOtherCallback).onTaskProfileLocked(eq(info));
+ mImpl.onTaskProfileLocked(info, 0);
+ verify(mCallback).onTaskProfileLocked(eq(info), eq(0));
+ verify(mOtherCallback).onTaskProfileLocked(eq(info), eq(0));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index 63de74fa3b05..d6387ee5ae13 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -82,6 +83,8 @@ import java.util.Arrays;
@RunWith(AndroidTestingRunner.class)
public class DesktopModeControllerTest extends ShellTestCase {
+ private static final int SECOND_DISPLAY = 2;
+
@Mock
private ShellController mShellController;
@Mock
@@ -248,22 +251,22 @@ public class DesktopModeControllerTest extends ShellTestCase {
public void testShowDesktopApps_allAppsInvisible_bringsToFront() {
// Set up two active tasks on desktop, task2 is on top of task1.
RunningTaskInfo freeformTask1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId);
mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- freeformTask1.taskId, false /* visible */);
+ DEFAULT_DISPLAY, freeformTask1.taskId, false /* visible */);
RunningTaskInfo freeformTask2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId);
mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- freeformTask2.taskId, false /* visible */);
+ DEFAULT_DISPLAY, freeformTask2.taskId, false /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn(
freeformTask1);
when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn(
freeformTask2);
// Run show desktop apps logic
- mController.showDesktopApps();
+ mController.showDesktopApps(DEFAULT_DISPLAY);
final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
// Check wct has reorder calls
@@ -283,17 +286,19 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Test
public void testShowDesktopApps_appsAlreadyVisible_bringsToFront() {
final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ true /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ true /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
- mController.showDesktopApps();
+ mController.showDesktopApps(DEFAULT_DISPLAY);
final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
// Check wct has reorder calls
@@ -312,17 +317,19 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Test
public void testShowDesktopApps_someAppsInvisible_reordersAll() {
final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, false /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ false /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ true /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
- mController.showDesktopApps();
+ mController.showDesktopApps(DEFAULT_DISPLAY);
final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
// Both tasks should be reordered to top, even if one was already visible.
@@ -336,38 +343,87 @@ public class DesktopModeControllerTest extends ShellTestCase {
}
@Test
+ public void testShowDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
+ RunningTaskInfo taskDefaultDisplay = createFreeformTask(DEFAULT_DISPLAY);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(
+ DEFAULT_DISPLAY, taskDefaultDisplay.taskId, false /* visible */);
+ when(mShellTaskOrganizer.getRunningTaskInfo(taskDefaultDisplay.taskId)).thenReturn(
+ taskDefaultDisplay);
+
+ RunningTaskInfo taskSecondDisplay = createFreeformTask(SECOND_DISPLAY);
+ mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(
+ SECOND_DISPLAY, taskSecondDisplay.taskId, false /* visible */);
+ when(mShellTaskOrganizer.getRunningTaskInfo(taskSecondDisplay.taskId)).thenReturn(
+ taskSecondDisplay);
+
+ mController.showDesktopApps(DEFAULT_DISPLAY);
+
+ WindowContainerTransaction wct = getBringAppsToFrontTransaction();
+ assertThat(wct.getHierarchyOps()).hasSize(1);
+ HierarchyOp op = wct.getHierarchyOps().get(0);
+ assertThat(op.getContainer()).isEqualTo(taskDefaultDisplay.token.asBinder());
+ }
+
+ @Test
public void testGetVisibleTaskCount_noTasks_returnsZero() {
- assertThat(mController.getVisibleTaskCount()).isEqualTo(0);
+ assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0);
}
@Test
public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() {
RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ true /* visible */);
RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ true /* visible */);
- assertThat(mController.getVisibleTaskCount()).isEqualTo(2);
+ assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2);
}
@Test
public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() {
RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ true /* visible */);
RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, false /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ false /* visible */);
- assertThat(mController.getVisibleTaskCount()).isEqualTo(1);
+ assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1);
+ }
+
+ @Test
+ public void testGetVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
+ RunningTaskInfo taskDefaultDisplay = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY,
+ taskDefaultDisplay.taskId,
+ true /* visible */);
+
+ RunningTaskInfo taskSecondDisplay = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(SECOND_DISPLAY,
+ taskSecondDisplay.taskId,
+ true /* visible */);
+
+ assertThat(mController.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 45cb3a062cc5..3bc2f0e8674e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -17,10 +17,12 @@
package com.android.wm.shell.desktopmode
import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.fail
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,8 +43,8 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
assertThat(repo.isActiveTask(1)).isTrue()
}
@@ -51,9 +53,9 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
- repo.addActiveTask(1)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
}
@Test
@@ -61,9 +63,22 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
- repo.addActiveTask(2)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
+ }
+
+ @Test
+ fun addActiveTask_multipleDisplays_notifiesCorrectListener() {
+ val listener = TestListener()
+ repo.addActiveTaskListener(listener)
+
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2)
+ repo.addActiveTask(SECOND_DISPLAY, taskId = 3)
+
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
+ assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(1)
}
@Test
@@ -71,10 +86,10 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
repo.removeActiveTask(1)
// Notify once for add and once for remove
- assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
assertThat(repo.isActiveTask(1)).isFalse()
}
@@ -83,7 +98,17 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
val listener = TestListener()
repo.addActiveTaskListener(listener)
repo.removeActiveTask(99)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(0)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(0)
+ }
+
+ @Test
+ fun remoteActiveTask_listenerForOtherDisplayNotNotified() {
+ val listener = TestListener()
+ repo.addActiveTaskListener(listener)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.removeActiveTask(1)
+ assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(0)
+ assertThat(repo.isActiveTask(1)).isFalse()
}
@Test
@@ -93,14 +118,27 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
@Test
fun addListener_notifiesVisibleFreeformTask() {
- repo.updateVisibleFreeformTasks(1, true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
executor.flushAll()
- assertThat(listener.hasVisibleFreeformTasks).isTrue()
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(1)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse()
+ // One call as adding listener notifies it
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(0)
}
@Test
@@ -108,13 +146,61 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
- repo.updateVisibleFreeformTasks(1, true)
- repo.updateVisibleFreeformTasks(2, true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun updateVisibleFreeformTasks_addVisibleTaskNotifiesListenerForThatDisplay() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isFalse()
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(0)
+
+ repo.updateVisibleFreeformTasks(displayId = 1, taskId = 2, visible = true)
+ executor.flushAll()
+
+ // Listener for secondary display is notified
+ assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isTrue()
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1)
+ // No changes to listener for default display
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun updateVisibleFreeformTasks_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
executor.flushAll()
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+
+ // Mark task 1 visible on secondary display
+ repo.updateVisibleFreeformTasks(displayId = 1, taskId = 1, visible = true)
+ executor.flushAll()
+
+ // Default display should have 2 calls
+ // 1 - visible task added
+ // 2 - visible task removed
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse()
- assertThat(listener.hasVisibleFreeformTasks).isTrue()
- // Equal to 2 because adding the listener notifies the current state
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2)
+ // Secondary display should have 1 call for visible task added
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1)
+ assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isTrue()
}
@Test
@@ -122,52 +208,83 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
- repo.updateVisibleFreeformTasks(1, true)
- repo.updateVisibleFreeformTasks(2, true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
executor.flushAll()
- assertThat(listener.hasVisibleFreeformTasks).isTrue()
- repo.updateVisibleFreeformTasks(1, false)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
executor.flushAll()
- // Equal to 2 because adding the listener notifies the current state
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2)
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
- repo.updateVisibleFreeformTasks(2, false)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false)
executor.flushAll()
- assertThat(listener.hasVisibleFreeformTasks).isFalse()
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(3)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
}
@Test
fun getVisibleTaskCount() {
// No tasks, count is 0
- assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
// New task increments count to 1
- repo.updateVisibleFreeformTasks(taskId = 1, visible = true)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
// Visibility update to same task does not increase count
- repo.updateVisibleFreeformTasks(taskId = 1, visible = true)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
// Second task visible increments count
- repo.updateVisibleFreeformTasks(taskId = 2, visible = true)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(2)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
// Hiding a task decrements count
- repo.updateVisibleFreeformTasks(taskId = 1, visible = false)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
// Hiding all tasks leaves count at 0
- repo.updateVisibleFreeformTasks(taskId = 2, visible = false)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false)
+ assertThat(repo.getVisibleTaskCount(displayId = 9)).isEqualTo(0)
// Hiding a not existing task, count remains at 0
- repo.updateVisibleFreeformTasks(taskId = 999, visible = false)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 999, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ }
+
+ @Test
+ fun getVisibleTaskCount_multipleDisplays() {
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
+
+ // New task on default display increments count for that display only
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
+
+ // New task on secondary display, increments count for that display only
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 2, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
+
+ // Marking task visible on another display, updates counts for both displays
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
+
+ // Marking task that is on secondary display, hidden on default display, does not affect
+ // secondary display
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
+
+ // Hiding a task on that display, decrements count
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
}
@Test
@@ -197,19 +314,40 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
}
class TestListener : DesktopModeTaskRepository.ActiveTasksListener {
- var activeTaskChangedCalls = 0
- override fun onActiveTasksChanged() {
- activeTaskChangedCalls++
+ var activeChangesOnDefaultDisplay = 0
+ var activeChangesOnSecondaryDisplay = 0
+ override fun onActiveTasksChanged(displayId: Int) {
+ when (displayId) {
+ DEFAULT_DISPLAY -> activeChangesOnDefaultDisplay++
+ SECOND_DISPLAY -> activeChangesOnSecondaryDisplay++
+ else -> fail("Active task listener received unexpected display id: $displayId")
+ }
}
}
class TestVisibilityListener : DesktopModeTaskRepository.VisibleTasksListener {
- var hasVisibleFreeformTasks = false
- var visibleFreeformTaskChangedCalls = 0
-
- override fun onVisibilityChanged(hasVisibleTasks: Boolean) {
- hasVisibleFreeformTasks = hasVisibleTasks
- visibleFreeformTaskChangedCalls++
+ var hasVisibleTasksOnDefaultDisplay = false
+ var hasVisibleTasksOnSecondaryDisplay = false
+
+ var visibleChangesOnDefaultDisplay = 0
+ var visibleChangesOnSecondaryDisplay = 0
+
+ override fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {
+ when (displayId) {
+ DEFAULT_DISPLAY -> {
+ hasVisibleTasksOnDefaultDisplay = hasVisibleFreeformTasks
+ visibleChangesOnDefaultDisplay++
+ }
+ SECOND_DISPLAY -> {
+ hasVisibleTasksOnSecondaryDisplay = hasVisibleFreeformTasks
+ visibleChangesOnSecondaryDisplay++
+ }
+ else -> fail("Visible task listener received unexpected display id: $displayId")
+ }
}
}
+
+ companion object {
+ const val SECOND_DISPLAY = 1
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index c9bd695ffb33..f506969f51df 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -25,6 +25,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.os.Binder
import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
@@ -84,10 +85,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
- lateinit var mockitoSession: StaticMockitoSession
- lateinit var controller: DesktopTasksController
- lateinit var shellInit: ShellInit
- lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+ private lateinit var mockitoSession: StaticMockitoSession
+ private lateinit var controller: DesktopTasksController
+ private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
// Mock running tasks are registered here so we can get the list from mock shell task organizer
private val runningTasks = mutableListOf<RunningTaskInfo>()
@@ -155,7 +156,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskHidden(task1)
markTaskHidden(task2)
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(3)
@@ -173,7 +174,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskVisible(task1)
markTaskVisible(task2)
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(3)
@@ -191,7 +192,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskHidden(task1)
markTaskVisible(task2)
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(3)
@@ -205,7 +206,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
val homeTask = setUpHomeTask()
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(1)
@@ -213,8 +214,26 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
+ val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
+ val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+ setUpHomeTask(SECOND_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(taskDefaultDisplay)
+ markTaskHidden(taskSecondDisplay)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY)
+
+ val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Expect order to be from bottom: home, task
+ wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
+ wct.assertReorderAt(index = 1, taskDefaultDisplay)
+ }
+
+ @Test
fun getVisibleTaskCount_noTasks_returnsZero() {
- assertThat(controller.getVisibleTaskCount()).isEqualTo(0)
+ assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
}
@Test
@@ -222,7 +241,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
setUpHomeTask()
setUpFreeformTask().also(::markTaskVisible)
setUpFreeformTask().also(::markTaskVisible)
- assertThat(controller.getVisibleTaskCount()).isEqualTo(2)
+ assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
}
@Test
@@ -230,7 +249,15 @@ class DesktopTasksControllerTest : ShellTestCase() {
setUpHomeTask()
setUpFreeformTask().also(::markTaskVisible)
setUpFreeformTask().also(::markTaskHidden)
- assertThat(controller.getVisibleTaskCount()).isEqualTo(1)
+ assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+ }
+
+ @Test
+ fun getVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
+ setUpHomeTask()
+ setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible)
+ setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible)
+ assertThat(controller.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
}
@Test
@@ -258,6 +285,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToDesktop(fullscreenTask)
with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ // Operations should include home task, freeform task
assertThat(hierarchyOps).hasSize(3)
assertReorderSequence(homeTask, freeformTask, fullscreenTask)
assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
@@ -266,6 +294,28 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
+ setUpHomeTask(displayId = DEFAULT_DISPLAY)
+ val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ markTaskHidden(freeformTaskDefault)
+
+ val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
+ val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ markTaskHidden(freeformTaskSecond)
+
+ controller.moveToDesktop(fullscreenTaskDefault)
+
+ with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ // Check that hierarchy operations do not include tasks from second display
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(homeTaskSecond.token.asBinder())
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(freeformTaskSecond.token.asBinder())
+ }
+ }
+
+ @Test
fun moveToFullscreen() {
val task = setUpFreeformTask()
controller.moveToFullscreen(task)
@@ -281,6 +331,19 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
+ val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
+
+ controller.moveToFullscreen(taskDefaultDisplay)
+
+ with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
+ assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
+ }
+ }
+
+ @Test
fun getTaskWindowingMode() {
val fullscreenTask = setUpFullscreenTask()
val freeformTask = setUpFreeformTask()
@@ -324,6 +387,18 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
+ assertThat(result).isNull()
+ }
+
+ @Test
fun handleRequest_freeformTask_freeformVisible_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -362,6 +437,18 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun handleRequest_freeformTask_freeformOnOtherDisplayOnly_returnSwitchToFullscreenWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+ assertThat(result?.changes?.get(taskDefaultDisplay.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+
+ @Test
fun handleRequest_notOpenOrToFrontTransition_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -400,35 +487,43 @@ class DesktopTasksControllerTest : ShellTestCase() {
assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
}
- private fun setUpFreeformTask(): RunningTaskInfo {
- val task = createFreeformTask()
+ private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createFreeformTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- desktopModeTaskRepository.addActiveTask(task.taskId)
+ desktopModeTaskRepository.addActiveTask(displayId, task.taskId)
desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId)
runningTasks.add(task)
return task
}
- private fun setUpHomeTask(): RunningTaskInfo {
- val task = createHomeTask()
+ private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createHomeTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
}
- private fun setUpFullscreenTask(): RunningTaskInfo {
- val task = createFullscreenTask()
+ private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createFullscreenTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
}
private fun markTaskVisible(task: RunningTaskInfo) {
- desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = true)
+ desktopModeTaskRepository.updateVisibleFreeformTasks(
+ task.displayId,
+ task.taskId,
+ visible = true
+ )
}
private fun markTaskHidden(task: RunningTaskInfo) {
- desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = false)
+ desktopModeTaskRepository.updateVisibleFreeformTasks(
+ task.displayId,
+ task.taskId,
+ visible = false
+ )
}
private fun getLatestWct(
@@ -457,6 +552,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
): TransitionRequestInfo {
return TransitionRequestInfo(type, task, null /* remoteTransition */)
}
+
+ companion object {
+ const val SECOND_DISPLAY = 2
+ }
}
private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index dc91d756842e..cf1ff3214d87 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -21,14 +21,17 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.view.Display.DEFAULT_DISPLAY
import com.android.wm.shell.TestRunningTaskInfoBuilder
class DesktopTestHelpers {
companion object {
/** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */
@JvmStatic
- fun createFreeformTask(): RunningTaskInfo {
+ @JvmOverloads
+ fun createFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
.setToken(MockToken().token())
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setWindowingMode(WINDOWING_MODE_FREEFORM)
@@ -38,8 +41,10 @@ class DesktopTestHelpers {
/** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */
@JvmStatic
- fun createFullscreenTask(): RunningTaskInfo {
+ @JvmOverloads
+ fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
.setToken(MockToken().token())
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
@@ -49,8 +54,10 @@ class DesktopTestHelpers {
/** Create a new home task */
@JvmStatic
- fun createHomeTask(): RunningTaskInfo {
+ @JvmOverloads
+ fun createHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
.setToken(MockToken().token())
.setActivityType(ACTIVITY_TYPE_HOME)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 6995d10dd78d..04f2c99783da 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -268,7 +268,7 @@ public class PipControllerTest extends ShellTestCase {
}
@Test
- public void saveReentryState_userHasResized_savesSize() {
+ public void saveReentryState_nonEmptyUserResizeBounds_savesSize() {
final Rect bounds = new Rect(0, 0, 10, 10);
final Rect resizedBounds = new Rect(0, 0, 30, 30);
when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
@@ -281,6 +281,19 @@ public class PipControllerTest extends ShellTestCase {
}
@Test
+ public void saveReentryState_emptyUserResizeBounds_savesSize() {
+ final Rect bounds = new Rect(0, 0, 10, 10);
+ final Rect resizedBounds = new Rect(0, 0, 0, 0);
+ when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
+ when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
+ when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
+
+ mPipController.saveReentryState(bounds);
+
+ verify(mMockPipBoundsState).saveReentryState(new Size(10, 10), 1.0f);
+ }
+
+ @Test
public void onDisplayConfigurationChanged_inPip_movePip() {
final int displayId = 1;
final Rect bounds = new Rect(0, 0, 10, 10);
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
index 6bfcd82b0a4a..8c35da13e2c4 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
@@ -58,6 +58,7 @@
android:textSize="14sp"
android:layout_marginTop="2dp"
style="@style/TextAppearance"
+ android:focusable="true"
android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 74072e9d4ec3..2502bbf7b40b 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -113,17 +113,11 @@
<!-- Back button for the helper consent dialog [CHAR LIMIT=30] -->
<string name="consent_back">Back</string>
- <!-- Action when permission list view is expanded CHAR LIMIT=30] -->
- <string name="permission_expanded">Expanded</string>
+ <!-- Expand permission in the list CHAR LIMIT=30] -->
+ <string name="permission_expand">Expand <xliff:g id="permission_type" example="Notification">%1$s</xliff:g></string>
- <!-- Expand action permission list CHAR LIMIT=30] -->
- <string name="permission_expand">Expand</string>
-
- <!-- Action when permission list view is collapsed CHAR LIMIT=30] -->
- <string name="permission_collapsed">Collapsed</string>
-
- <!-- Collapse action permission list CHAR LIMIT=30] -->
- <string name="permission_collapse">Collapse</string>
+ <!-- Collapse permission int the list CHAR LIMIT=30] -->
+ <string name="permission_collapse">Collapse <xliff:g id="permission_type" example="Notification">%1$s</xliff:g></string>
<!-- ================== System data transfer ==================== -->
<!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=NONE] -->
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
index b86ef649331a..f594bf270d29 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
@@ -124,7 +124,7 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V
}
setAccessibility(view, viewType,
- AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand);
+ AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand, 0);
// Add expand buttons if the permissions are more than PERMISSION_SIZE in this list also
// make the summary invisible by default.
@@ -137,18 +137,16 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V
viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_less);
viewHolder.mPermissionSummary.setVisibility(View.VISIBLE);
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_less);
- view.setContentDescription(mContext.getString(R.string.permission_expanded));
setAccessibility(view, viewType,
- AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_collapse);
- viewHolder.mPermissionSummary.setFocusable(true);
+ AccessibilityNodeInfo.ACTION_CLICK,
+ R.string.permission_collapse, R.string.permission_expand);
} else {
viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_more);
viewHolder.mPermissionSummary.setVisibility(View.GONE);
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more);
- view.setContentDescription(mContext.getString(R.string.permission_collapsed));
setAccessibility(view, viewType,
- AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expanded);
- viewHolder.mPermissionSummary.setFocusable(false);
+ AccessibilityNodeInfo.ACTION_CLICK,
+ R.string.permission_expand, R.string.permission_collapse);
}
});
} else {
@@ -200,14 +198,20 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V
}
}
- private void setAccessibility(View view, int viewType, int action, int resourceId) {
- final String actionString = mContext.getString(resourceId);
+ private void setAccessibility(View view, int viewType, int action, int statusResourceId,
+ int actionResourceId) {
final String permission = mContext.getString(sTitleMap.get(viewType));
+
+ if (actionResourceId != 0) {
+ view.announceForAccessibility(
+ getHtmlFromResources(mContext, actionResourceId, permission));
+ }
+
view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(action,
- actionString + permission));
+ getHtmlFromResources(mContext, statusResourceId, permission)));
}
});
}
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index a3b27529d857..340285760cda 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -86,6 +86,8 @@
<string name="use_provider_for_all_description">This password manager for <xliff:g id="username" example="becket@gmail.com">%1$s</xliff:g> will store your passwords and passkeys to help you easily sign in</string>
<!-- This is a label for a button that sets this password manager as the default. [CHAR LIMIT=20] -->
<string name="set_as_default">Set as default</string>
+ <!-- This is a button text to navigate the user to their system settings. [CHAR LIMIT=30] -->
+ <string name="settings">Settings</string>
<!-- This is a label for a button that makes this password manager be used just in this specific case. [CHAR LIMIT=20] -->
<string name="use_once">Use once</string>
<!-- Appears as an option row subtitle to show how many passwords and passkeys are saved in this option when there are passwords and passkeys. [CHAR LIMIT=80] -->
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 01f92c4fa7b1..19b7e8546805 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -17,6 +17,7 @@
package com.android.settingslib.collapsingtoolbar;
import android.app.ActionBar;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -59,7 +60,8 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (mCustomizeLayoutResId > 0 && !BuildCompatUtils.isAtLeastS()) {
+ // for backward compatibility on R devices or wearable devices due to small device size.
+ if (mCustomizeLayoutResId > 0 && (!BuildCompatUtils.isAtLeastS() || isWatch())) {
super.setContentView(mCustomizeLayoutResId);
return;
}
@@ -157,6 +159,14 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
return getToolbarDelegate().getAppBarLayout();
}
+ private boolean isWatch() {
+ PackageManager packageManager = getPackageManager();
+ if (packageManager == null) {
+ return false;
+ }
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
private CollapsingToolbarDelegate getToolbarDelegate() {
if (mToolbardelegate == null) {
mToolbardelegate = new CollapsingToolbarDelegate(new DelegateCallback());
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
index 3c451127508d..dae48dbac0a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -17,6 +17,7 @@
package com.android.settingslib.applications;
import android.app.AppGlobals;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -94,6 +95,10 @@ public class DefaultAppInfo extends CandidateInfo {
}
+ public @Nullable String getSummary() {
+ return this.summary;
+ }
+
@Override
public Drawable loadIcon() {
final IconDrawableFactory factory = IconDrawableFactory.newInstance(mContext);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index e4cc9f15aea1..6a5535d345db 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -100,6 +100,5 @@ public class SystemSettings {
Settings.System.CAMERA_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
- Settings.System.SMOOTH_DISPLAY
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4b720636c1d4..85623b26c589 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -226,6 +226,5 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR);
- VALIDATORS.put(System.SMOOTH_DISPLAY, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d1bd5e661072..46b45d12efc8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -34,7 +34,6 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OV
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM;
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
import static com.android.providers.settings.SettingsState.getTypeFromKey;
import static com.android.providers.settings.SettingsState.getUserIdFromKey;
@@ -5674,7 +5673,7 @@ public class SettingsProvider extends ContentProvider {
providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default array Cred Provider not found: " + e.toString());
+ "Get default array Cred Provider not found: " + e.toString());
}
try {
final String storedValue = resources.getString(resourceId);
@@ -5683,7 +5682,7 @@ public class SettingsProvider extends ContentProvider {
}
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default Cred Provider not found: " + e.toString());
+ "Get default Cred Provider not found: " + e.toString());
}
if (!providers.isEmpty()) {
@@ -5732,8 +5731,8 @@ public class SettingsProvider extends ContentProvider {
final Setting currentSetting = secureSettings
.getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
if (currentSetting.isNull()) {
- final int resourceId = com.android.internal.R.array
- .config_defaultCredentialProviderService;
+ final int resourceId =
+ com.android.internal.R.array.config_defaultCredentialProviderService;
final Resources resources = getContext().getResources();
// If the config has not be defined we might get an exception.
final List<String> providers = new ArrayList<>();
@@ -5741,7 +5740,7 @@ public class SettingsProvider extends ContentProvider {
providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default array Cred Provider not found: " + e.toString());
+ "Get default array Cred Provider not found: " + e.toString());
}
if (!providers.isEmpty()) {
@@ -5840,44 +5839,12 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 218;
}
- // v218: Convert Smooth Display and Force Peak Refresh Rate to a boolean
if (currentVersion == 218) {
- final String peakRefreshRateSettingName = "peak_refresh_rate";
- final String minRefreshRateSettingName = "min_refresh_rate";
-
- final SettingsState systemSettings = getSystemSettingsLocked(userId);
- final Setting peakRefreshRateSetting =
- systemSettings.getSettingLocked(peakRefreshRateSettingName);
- final Setting minRefreshRateSetting =
- systemSettings.getSettingLocked(minRefreshRateSettingName);
-
- float peakRefreshRate = DEFAULT_REFRESH_RATE;
- float minRefreshRate = 0;
- try {
- if (!peakRefreshRateSetting.isNull()) {
- peakRefreshRate = Float.parseFloat(peakRefreshRateSetting.getValue());
- }
- } catch (NumberFormatException e) {
- // Do nothing. Overwrite with default value.
- }
- try {
- if (!minRefreshRateSetting.isNull()) {
- minRefreshRate = Float.parseFloat(minRefreshRateSetting.getValue());
- }
- } catch (NumberFormatException e) {
- // Do nothing. Overwrite with default value.
- }
-
- systemSettings.deleteSettingLocked(peakRefreshRateSettingName);
- systemSettings.deleteSettingLocked(minRefreshRateSettingName);
-
- systemSettings.insertSettingLocked(Settings.System.SMOOTH_DISPLAY,
- peakRefreshRate > DEFAULT_REFRESH_RATE ? "1" : "0", /* tag= */ null,
- /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME);
- systemSettings.insertSettingLocked(Settings.System.FORCE_PEAK_REFRESH_RATE,
- minRefreshRate > 0 ? "1" : "0", /* tag= */ null,
- /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME);
-
+ // Version 219: Removed
+ // TODO(b/211737588): Back up the Smooth Display setting
+ // Future upgrades to the `peak_refresh_rate` and `min_refresh_rate` settings
+ // should account for the database in a non-upgraded and upgraded (change id:
+ // Ib2cb2dd100f06f5452083b7606109a486e795a0e) state.
currentVersion = 219;
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 36aa2ac74406..706666cbebab 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -97,7 +97,8 @@ public class SettingsBackupTest {
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
Settings.System.DESKTOP_MODE, // developer setting for internal prototyping
- Settings.System.FORCE_PEAK_REFRESH_RATE, // depends on hardware capabilities
+ Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
+ Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6e55000dfe8a..c8eb4b41f003 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -992,6 +992,18 @@
android:excludeFromRecents="true"
android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay" />
+
+ <activity
+ android:name=".notetask.LaunchNotesRoleSettingsTrampolineActivity"
+ android:exported="true"
+ android:excludeFromRecents="true"
+ android:resizeableActivity="false"
+ android:theme="@android:style/Theme.NoDisplay" >
+ <intent-filter>
+ <action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<!-- endregion -->
<!-- started from ControlsRequestReceiver -->
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 77ddc6e573b7..1ce347215954 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -55,6 +55,7 @@ lynhan@google.com
madym@google.com
mankoff@google.com
mateuszc@google.com
+mgalhardo@google.com
michaelmikhil@google.com
michschn@google.com
mkephart@google.com
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 436145ec0692..3244eb43c8c4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -131,7 +131,8 @@ public interface QS extends FragmentBase {
/**
* A rounded corner clipping that makes QS feel as if it were behind everything.
*/
- void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible);
+ void setFancyClipping(int leftInset, int top, int rightInset, int bottom, int cornerRadius,
+ boolean visible, boolean fullWidth);
/**
* @return if quick settings is fully collapsed currently
diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
index 29832a081612..934fa6f54286 100644
--- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
+++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
@@ -30,7 +30,7 @@
<FrameLayout
android:id="@+id/inout_container"
- android:layout_height="17dp"
+ android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical">
<ImageView
@@ -39,24 +39,25 @@
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_down"
android:visibility="gone"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
/>
<ImageView
android:id="@+id/mobile_out"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_up"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
android:visibility="gone"
/>
</FrameLayout>
<ImageView
android:id="@+id/mobile_type"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/status_bar_mobile_signal_size"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:paddingStart="2.5dp"
- android:paddingEnd="1dp"
+ android:adjustViewBounds="true"
+ android:paddingStart="2.5sp"
+ android:paddingEnd="1sp"
android:visibility="gone" />
<Space
android:id="@+id/mobile_roaming_space"
@@ -70,14 +71,14 @@
android:layout_gravity="center_vertical">
<com.android.systemui.statusbar.AnimatedImageView
android:id="@+id/mobile_signal"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_height="@dimen/status_bar_mobile_signal_size"
+ android:layout_width="@dimen/status_bar_mobile_signal_size"
systemui:hasOverlappingRendering="false"
/>
<ImageView
android:id="@+id/mobile_roaming"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/status_bar_mobile_signal_size"
+ android:layout_height="@dimen/status_bar_mobile_signal_size"
android:src="@drawable/stat_sys_roaming"
android:contentDescription="@string/data_connection_roaming"
android:visibility="gone" />
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 4fc411eaea9d..4d289ebeb30b 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -22,6 +22,7 @@
<!-- Keyguard PIN pad styles -->
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
<item name="android:textColor">?androidprv:attr/materialColorOnTertiaryFixed</item>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
index ee8d488343c4..a35504f9c202 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
@@ -16,16 +16,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:fillAlpha="1"
- android:fillColor="@android:color/white"
- android:fillType="nonZero"
- android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
+ android:pathData="M23.41,6L22,4.59C21.63,4.21 21.12,4 20.59,4C20.06,4 19.55,4.21 19.18,4.59L11.39,12.38L9.09,14.68L8.04,18.9C8.01,18.96 8,19.04 8,19.11C8,19.6 8.4,20 8.89,20C8.96,20 9.04,19.99 9.11,19.97L13.33,18.92L15.63,16.62L23.42,8.83C23.79,8.45 24,7.94 24,7.41C24,6.88 23.79,6.37 23.41,6ZM14.21,15.21L13.21,16.21L11.8,14.8L12.8,13.8L20.59,6L22,7.41L14.21,15.21Z"
+ android:fillColor="@android:color/white"/>
<path
- android:fillAlpha="1"
- android:fillColor="@android:color/white"
- android:fillType="nonZero"
- android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
+ android:pathData="M6.688,20C2.047,20 0.333,18.65 0.333,16C0.333,13.61 2.439,12.474 5.713,12C6.792,11.844 7.344,11.397 7.344,10.927C7.344,9.625 4.679,9.705 3.833,9.667V7.667C3.833,7.667 6.792,7.667 8.208,8.729C8.932,9.272 9.333,9.979 9.333,11.05C9.333,12.52 8.281,13.677 5.713,13.885C4.017,14.023 2.333,14.52 2.333,16C2.333,17.33 4.013,18 7.333,18L6.688,20Z"
+ android:fillColor="@android:color/white"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
index 759018289562..860fc7d6b316 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
@@ -13,19 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <path
- android:fillAlpha="1"
- android:fillColor="#636C6F"
- android:fillType="nonZero"
- android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
- <path
- android:fillAlpha="1"
- android:fillColor="#636C6F"
- android:fillType="nonZero"
- android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
-</vector>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_note_task_shortcut_widget_background" />
+ <foreground android:drawable="@drawable/ic_note_task_shortcut_widget_foreground" />
+</adaptive-icon>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml
new file mode 100644
index 000000000000..9f98f07c8a58
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:pathData="M0,0h108v108h-108z"
+ android:fillColor="#0B57D0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml
new file mode 100644
index 000000000000..fcb3ef4bfdc5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:pathData="M74.92,43L72.33,40.42C71.65,39.72 70.72,39.33 69.75,39.33C68.78,39.33 67.84,39.72 67.16,40.42L52.88,54.7L48.67,58.91L46.74,66.65C46.69,66.76 46.67,66.91 46.67,67.04C46.67,67.93 47.4,68.67 48.3,68.67C48.43,68.67 48.57,68.65 48.7,68.61L56.44,66.69L60.65,62.47L74.94,48.19C75.61,47.49 76,46.56 76,45.58C76,44.61 75.61,43.68 74.92,43ZM58.05,59.88L56.22,61.72L53.63,59.13L55.47,57.3L69.75,43L72.33,45.58L58.05,59.88Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M44.26,68.67C35.75,68.67 32.61,66.19 32.61,61.33C32.61,56.95 36.47,54.87 42.47,54C44.45,53.71 45.46,52.89 45.46,52.03C45.46,49.65 40.58,49.79 39.03,49.72V46.06C39.03,46.06 44.45,46.06 47.05,48C48.37,49 49.11,50.3 49.11,52.26C49.11,54.95 47.18,57.07 42.47,57.46C39.36,57.71 36.28,58.62 36.28,61.33C36.28,63.77 39.36,65 45.44,65L44.26,68.67Z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 441f963a855a..e989372adde3 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -126,8 +126,7 @@
<com.android.systemui.battery.BatteryMeterView
android:id="@+id/batteryRemainingIcon"
android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
+ android:layout_height="0dp"
app:layout_constrainedWidth="true"
app:textAppearance="@style/TextAppearance.QS.Status"
app:layout_constraintStart_toEndOf="@id/statusIcons"
diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
index 0ea0653ab89f..473ab08a1935 100644
--- a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
+++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
@@ -24,11 +24,11 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:layout_marginStart="2.5dp"
+ android:layout_marginStart="2.5sp"
>
<FrameLayout
android:id="@+id/inout_container"
- android:layout_height="17dp"
+ android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size"
android:layout_width="wrap_content"
android:gravity="center_vertical" >
<ImageView
@@ -37,14 +37,14 @@
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_down"
android:visibility="gone"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
/>
<ImageView
android:id="@+id/wifi_out"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_up"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
android:visibility="gone"
/>
</FrameLayout>
@@ -62,7 +62,7 @@
<View
android:id="@+id/wifi_signal_spacer"
android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
- android:layout_height="4dp"
+ android:layout_height="4sp"
android:visibility="gone" />
<!-- Looks like CarStatusBar uses this... -->
@@ -75,7 +75,7 @@
<View
android:id="@+id/wifi_airplane_spacer"
android:layout_width="@dimen/status_bar_airplane_spacer_width"
- android:layout_height="4dp"
+ android:layout_height="4sp"
android:visibility="gone"
/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json
new file mode 100644
index 000000000000..49c1c40f7f4f
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Landscape_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":13,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 2","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-129,"s":[-67]},{"t":-29,"s":[0]}],"ix":10},"p":{"a":0,"k":[-75.352,41.307,0],"ix":2,"l":2},"a":{"a":0,"k":[94.648,211.307,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.72,-5.642],[0,0],[-9.394,-0.562],[-0.298,-0.038]],"o":[[-5.153,4.329],[3.882,-16.05],[0.31,0.019],[-0.044,0.75]],"v":[[0.863,12.222],[-8.931,14.755],[8.005,-15.108],[8.931,-15.021]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[81.486,130.081],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 9","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.459,6.045],[-5.153,4.329],[-0.044,0.75],[3.116,-24.664],[5.23,-22.052],[8.666,11.92],[-2.9,9.135]],"o":[[0,0],[6.72,-5.642],[12.723,1.335],[-2.369,18.762],[-13.993,-5.333],[2.255,-5.502],[1.843,-5.815]],"v":[[-9.99,-18.348],[-0.196,-20.881],[7.872,-48.124],[21.578,-9.331],[12.104,48.124],[-22.574,21.555],[-14.791,-0.206]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82.545,163.184],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 8","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"black circle matte 4","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"black circle matte 5","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey700","cl":"grey700","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json
new file mode 100644
index 000000000000..9ea0d35e1de2
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Portrait_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 3","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-2,"ix":10},"p":{"a":0,"k":[260.134,83.782,0],"ix":2,"l":2},"a":{"a":0,"k":[302.634,38.782,0],"ix":1,"l":2},"s":{"a":0,"k":[178,178,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.262,5.076],[0,0],[-0.424,-7.095],[-0.028,-0.225]],"o":[[3.269,-3.892],[-12.123,2.932],[0.015,0.234],[0.567,-0.034]],"v":[[9.232,0.652],[11.145,-6.746],[-11.412,6.046],[-11.346,6.746]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[241.281,55.033],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.565,-1.102],[3.269,-3.892],[0.566,-0.033],[-18.63,2.353],[-16.656,3.951],[9.004,6.546],[6.9,-2.19]],"o":[[0,0],[-4.262,5.076],[1.008,9.61],[14.171,-1.79],[-4.028,-10.569],[-4.156,1.703],[-4.392,1.392]],"v":[[-13.858,-7.546],[-15.771,-0.148],[-36.349,5.946],[-7.047,16.299],[36.349,9.142],[16.281,-17.051],[-0.156,-11.172]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[266.285,55.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"black circle matte 4","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"black circle matte 5","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey700","cl":"grey700","parent":16,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json
new file mode 100644
index 000000000000..f2b259335034
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Portrait_Reverse_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":13,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 2","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-75.352,41.307,0],"ix":2,"l":2},"a":{"a":0,"k":[94.648,211.307,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.72,-5.642],[0,0],[-9.394,-0.562],[-0.298,-0.038]],"o":[[-5.153,4.329],[3.882,-16.05],[0.31,0.019],[-0.044,0.75]],"v":[[0.863,12.222],[-8.931,14.755],[8.005,-15.108],[8.931,-15.021]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[81.486,130.081],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 9","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.459,6.045],[-5.153,4.329],[-0.044,0.75],[3.116,-24.664],[5.23,-22.052],[8.666,11.92],[-2.9,9.135]],"o":[[0,0],[6.72,-5.642],[12.723,1.335],[-2.369,18.762],[-13.993,-5.333],[2.255,-5.502],[1.843,-5.815]],"v":[[-9.99,-18.348],[-0.196,-20.881],[7.872,-48.124],[21.578,-9.331],[12.104,48.124],[-22.574,21.555],[-14.791,-0.206]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82.545,163.184],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 8","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"black circle matte 4","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"black circle matte 5","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey700","cl":"grey700","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 20864591ae5a..f40615eb46d0 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -17,7 +17,7 @@
-->
<resources>
<!-- gap on either side of status bar notification icons -->
- <dimen name="status_bar_icon_padding">1dp</dimen>
+ <dimen name="status_bar_icon_padding">1sp</dimen>
<dimen name="controls_header_horizontal_padding">28dp</dimen>
<dimen name="controls_content_margin_horizontal">40dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0aa880fe6d88..f5c4a4e4bb52 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -122,26 +122,26 @@
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
<!-- Default horizontal drawable padding for status bar icons. -->
- <dimen name="status_bar_horizontal_padding">2.5dp</dimen>
+ <dimen name="status_bar_horizontal_padding">2.5sp</dimen>
<!-- Height of the battery icon in the status bar. -->
- <dimen name="status_bar_battery_icon_height">13.0dp</dimen>
+ <dimen name="status_bar_battery_icon_height">13.0sp</dimen>
<!-- Width of the battery icon in the status bar. The battery drawable assumes a 12x20 canvas,
- so the width of the icon should be 13.0dp * (12.0 / 20.0) -->
- <dimen name="status_bar_battery_icon_width">7.8dp</dimen>
+ so the width of the icon should be 13.0sp * (12.0 / 20.0) -->
+ <dimen name="status_bar_battery_icon_width">7.8sp</dimen>
- <!-- The battery icon is 13dp tall, but the other system icons are 15dp tall (see
+ <!-- The battery icon is 13sp tall, but the other system icons are 15sp tall (see
@*android:dimen/status_bar_system_icon_size) with some top and bottom padding embedded in
- the drawables themselves. So, the battery icon may need an extra 1dp of spacing so that its
+ the drawables themselves. So, the battery icon may need an extra 1sp of spacing so that its
bottom still aligns with the bottom of all the other system icons. See b/258672854. -->
- <dimen name="status_bar_battery_extra_vertical_spacing">1dp</dimen>
+ <dimen name="status_bar_battery_extra_vertical_spacing">1sp</dimen>
<!-- The font size for the clock in the status bar. -->
<dimen name="status_bar_clock_size">14sp</dimen>
<!-- The starting padding for the clock in the status bar. -->
- <dimen name="status_bar_clock_starting_padding">7dp</dimen>
+ <dimen name="status_bar_clock_starting_padding">7sp</dimen>
<!-- The end padding for the clock in the status bar. -->
<dimen name="status_bar_clock_end_padding">0dp</dimen>
@@ -153,16 +153,19 @@
<dimen name="status_bar_left_clock_end_padding">2dp</dimen>
<!-- Spacing after the wifi signals that is present if there are any icons following it. -->
- <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen>
+ <dimen name="status_bar_wifi_signal_spacer_width">2.5sp</dimen>
<!-- Size of the view displaying the wifi signal icon in the status bar. -->
- <dimen name="status_bar_wifi_signal_size">@*android:dimen/status_bar_system_icon_size</dimen>
+ <dimen name="status_bar_wifi_signal_size">13sp</dimen>
+
+ <!-- Size of the view displaying the mobile signal icon in the status bar. -->
+ <dimen name="status_bar_mobile_signal_size">13sp</dimen>
<!-- Spacing before the airplane mode icon if there are any icons preceding it. -->
- <dimen name="status_bar_airplane_spacer_width">4dp</dimen>
+ <dimen name="status_bar_airplane_spacer_width">4sp</dimen>
<!-- Spacing between system icons. -->
- <dimen name="status_bar_system_icon_spacing">0dp</dimen>
+ <dimen name="status_bar_system_icon_spacing">2sp</dimen>
<!-- The amount to scale each of the status bar icons by. A value of 1 means no scaling. -->
<item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
@@ -310,7 +313,7 @@
<dimen name="snooze_snackbar_min_height">56dp</dimen>
<!-- size at which Notification icons will be drawn in the status bar -->
- <dimen name="status_bar_icon_drawing_size">15dp</dimen>
+ <dimen name="status_bar_icon_drawing_size">15sp</dimen>
<!-- size at which Notification icons will be drawn on Ambient Display -->
<dimen name="status_bar_icon_drawing_size_dark">
@@ -321,22 +324,22 @@
<item type="dimen" name="status_bar_icon_drawing_alpha">90%</item>
<!-- gap on either side of status bar notification icons -->
- <dimen name="status_bar_icon_padding">0dp</dimen>
+ <dimen name="status_bar_icon_padding">0sp</dimen>
<!-- the padding on the start of the statusbar -->
- <dimen name="status_bar_padding_start">8dp</dimen>
+ <dimen name="status_bar_padding_start">8sp</dimen>
<!-- the padding on the end of the statusbar -->
- <dimen name="status_bar_padding_end">8dp</dimen>
+ <dimen name="status_bar_padding_end">8sp</dimen>
<!-- the padding on the top of the statusbar (usually 0) -->
- <dimen name="status_bar_padding_top">0dp</dimen>
+ <dimen name="status_bar_padding_top">0sp</dimen>
<!-- the radius of the overflow dot in the status bar -->
- <dimen name="overflow_dot_radius">2dp</dimen>
+ <dimen name="overflow_dot_radius">2sp</dimen>
<!-- the padding between dots in the icon overflow -->
- <dimen name="overflow_icon_dot_padding">3dp</dimen>
+ <dimen name="overflow_icon_dot_padding">3sp</dimen>
<!-- Dimensions related to screenshots -->
@@ -617,8 +620,8 @@
<dimen name="qs_footer_icon_size">20dp</dimen>
<dimen name="qs_header_row_min_height">48dp</dimen>
- <dimen name="qs_header_non_clickable_element_height">24dp</dimen>
- <dimen name="new_qs_header_non_clickable_element_height">24dp</dimen>
+ <dimen name="qs_header_non_clickable_element_height">24sp</dimen>
+ <dimen name="new_qs_header_non_clickable_element_height">24sp</dimen>
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
@@ -822,7 +825,7 @@
<!-- Padding between the mobile signal indicator and the start icon when the roaming icon
is displayed in the upper left corner. -->
- <dimen name="roaming_icon_start_padding">2dp</dimen>
+ <dimen name="roaming_icon_start_padding">2sp</dimen>
<!-- Extra padding between the mobile data type icon and the strength indicator when the data
type icon is wide for the tile in quick settings. -->
@@ -1042,13 +1045,13 @@
<dimen name="display_cutout_margin_consumption">0px</dimen>
<!-- Height of the Ongoing App Ops chip -->
- <dimen name="ongoing_appops_chip_height">24dp</dimen>
+ <dimen name="ongoing_appops_chip_height">24sp</dimen>
<!-- Side padding between background of Ongoing App Ops chip and content -->
<dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
<!-- Margin between icons of Ongoing App Ops chip -->
<dimen name="ongoing_appops_chip_icon_margin">4dp</dimen>
<!-- Icon size of Ongoing App Ops chip -->
- <dimen name="ongoing_appops_chip_icon_size">16dp</dimen>
+ <dimen name="ongoing_appops_chip_icon_size">16sp</dimen>
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">28dp</dimen>
<!-- One or two privacy items -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 853930e174a7..a51afe905354 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1257,7 +1257,10 @@
<string name="monitoring_description_managed_profile_network_logging">Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile.</string>
<!-- Monitoring dialog: Description of an active VPN. [CHAR LIMIT=NONE]-->
- <string name="monitoring_description_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
+ <string name="monitoring_description_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to the VPN provider.</string>
+
+ <!-- Monitoring dialog: Description of an active VPN on a managed device. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_managed_device_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
<!-- Monitoring dialog: Description of two active VPNs. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_two_named_vpns">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> and <xliff:g id="vpn_app" example="Bar VPN App">%2$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
@@ -3035,6 +3038,19 @@
-->
<string name="keyguard_affordance_enablement_dialog_home_instruction_2">&#8226; At least one device is available</string>
+ <!---
+ Requirement for the notes app to be available for the user to use. This is shown as part of a
+ bulleted list of requirements. When all requirements are met, the app can be accessed through a
+ shortcut button on the lock screen. [CHAR LIMIT=NONE] -->
+ <string name="keyguard_affordance_enablement_dialog_notes_app_instruction">Select a default notes app to use notetaking shortcut</string>
+
+ <!---
+ The action to make the lock screen shortcut for the notes app to be available for the user to
+ use. This is shown as the action button in the dialog listing the requirements. When all
+ requirements are met, the app can be accessed through a shortcut button on the lock screen.
+ [CHAR LIMIT=NONE] -->
+ <string name="keyguard_affordance_enablement_dialog_notes_app_action">Open settings</string>
+
<!--
Error message shown when a shortcut must be pressed and held to activate it, usually shown when
the user tried to tap the shortcut or held it for too short a time. [CHAR LIMIT=32].
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 362d7a9390d4..7cf3121957ca 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -68,7 +68,7 @@ public interface TaskStackChangeListener {
onActivityLaunchOnSecondaryDisplayRerouted();
}
- default void onTaskProfileLocked(RunningTaskInfo taskInfo) { }
+ default void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { }
default void onTaskCreated(int taskId, ComponentName componentName) { }
default void onTaskRemoved(int taskId) { }
default void onTaskMovedToFront(int taskId) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index dd52cfbdc80f..c613afbda5b8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -262,8 +262,8 @@ public class TaskStackChangeListeners {
}
@Override
- public void onTaskProfileLocked(RunningTaskInfo taskInfo) {
- mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskInfo).sendToTarget();
+ public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) {
+ mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget();
}
@Override
@@ -418,8 +418,9 @@ public class TaskStackChangeListeners {
}
case ON_TASK_PROFILE_LOCKED: {
final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
+ final int userId = msg.arg1;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskProfileLocked(info);
+ mTaskStackListeners.get(i).onTaskProfileLocked(info, userId);
}
break;
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt b/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt
new file mode 100644
index 000000000000..af29b05a3fb1
--- /dev/null
+++ b/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.log
+
+import android.os.Build
+import android.util.Log
+import android.util.Log.LOG_ID_MAIN
+
+/**
+ * A simplified debug logger built as a wrapper around Android's [Log]. Internal for development.
+ *
+ * The main advantages are:
+ * - Sensible defaults, automatically retrieving the class name from the call-site (i.e., tag);
+ * - The messages are purged from source on release builds (keep in mind they are visible on AOSP);
+ * - Lazily evaluate Strings for zero impact in production builds or when disabled;
+ *
+ * Usage example:
+ * ```kotlin
+ * // Logging a message:
+ * debugLog { "message" }
+ *
+ * // Logging an error:
+ * debugLog(error = exception) { "message" }
+ *
+ * // Logging the current stack trace, for debugging:
+ * debugLog(error = Throwable()) { "message" }
+ * ```
+ */
+object DebugLogger {
+
+ /**
+ * Log a debug message, with sensible defaults.
+ *
+ * For example:
+ * ```kotlin
+ * val one = 1
+ * debugLog { "message#$one" }
+ * ```
+ *
+ * The output will be: `D/NoteTaskController: message#1`
+ *
+ * Beware, the [debugLog] content is **REMOVED FROM SOURCE AND BINARY** in Release builds.
+ *
+ * @param enabled: whether or not the message should be logged. By default, it is
+ * [Build.IS_DEBUGGABLE].
+ * @param priority: type of this log. By default, it is [Log.DEBUG].
+ * @param tag: identifies the source of a log. By default, it is the receiver's simple name.
+ * @param error: a [Throwable] to log.
+ * @param message: a lazily evaluated message you wish to log.
+ */
+ inline fun Any.debugLog(
+ enabled: Boolean = Build.IS_DEBUGGABLE,
+ priority: Int = Log.DEBUG,
+ tag: String = this::class.simpleName.orEmpty(),
+ error: Throwable? = null,
+ message: () -> String,
+ ) {
+ if (enabled) {
+ if (error == null) {
+ Log.println(priority, tag, message())
+ } else {
+ Log.printlns(LOG_ID_MAIN, priority, tag, message(), error)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt b/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt
new file mode 100644
index 000000000000..2764a1fdfe3d
--- /dev/null
+++ b/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.log
+
+import android.os.Build
+import android.util.Log
+
+/** An empty logger for release builds. */
+object DebugLogger {
+
+ @JvmName("logcatMessage")
+ inline fun Any.debugLog(
+ enabled: Boolean = Build.IS_DEBUGGABLE,
+ priority: Int = Log.DEBUG,
+ tag: String = this::class.simpleName.orEmpty(),
+ error: Throwable? = null,
+ message: () -> String,
+ ) {
+ // no-op.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 4b5c50ff7fa5..5499d2c07181 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -19,7 +19,6 @@ package com.android.systemui.biometrics
import android.annotation.RawRes
import android.content.Context
import android.content.Context.FINGERPRINT_SERVICE
-import android.content.res.Configuration
import android.hardware.fingerprint.FingerprintManager
import android.view.DisplayInfo
import android.view.Surface
@@ -35,21 +34,18 @@ import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR
import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE
import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
-import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
-import com.android.systemui.unfold.updates.FoldProvider
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
context: Context,
iconView: LottieAnimationView,
protected val iconViewOverlay: LottieAnimationView
-) : AuthIconController(context, iconView), FoldProvider.FoldCallback {
+) : AuthIconController(context, iconView) {
- private var isDeviceFolded: Boolean = false
private val isSideFps: Boolean
private val isReverseDefaultRotation =
context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
- private val screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
+
var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
@@ -77,20 +73,16 @@ open class AuthBiometricFingerprintIconController(
if (isSideFps && getRotationFromDefault(displayInfo.rotation) == Surface.ROTATION_180) {
iconView.rotation = 180f
}
- screenSizeFoldProvider.registerCallback(this, context.mainExecutor)
- screenSizeFoldProvider.onConfigurationChange(context.resources.configuration)
}
private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
val displayInfo = DisplayInfo()
context.display?.getDisplayInfo(displayInfo)
val rotation = getRotationFromDefault(displayInfo.rotation)
- val iconAnimation = getSideFpsAnimationForTransition(rotation)
val iconViewOverlayAnimation =
getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
- iconView.setAnimation(iconAnimation)
iconViewOverlay.setAnimation(iconViewOverlayAnimation)
}
@@ -132,10 +124,6 @@ open class AuthBiometricFingerprintIconController(
LottieColorUtils.applyDynamicColors(context, iconView)
}
- override fun onConfigurationChanged(newConfig: Configuration) {
- screenSizeFoldProvider.onConfigurationChange(newConfig)
- }
-
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
if (isSideFps) {
updateIconSideFps(lastState, newState)
@@ -230,25 +218,6 @@ open class AuthBiometricFingerprintIconController(
if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation
@RawRes
- private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
- Surface.ROTATION_90 -> if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_topleft
- } else {
- R.raw.biometricprompt_portrait_base_topleft
- }
- Surface.ROTATION_270 -> if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_bottomright
- } else {
- R.raw.biometricprompt_portrait_base_bottomright
- }
- else -> if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_default
- } else {
- R.raw.biometricprompt_landscape_base
- }
- }
-
- @RawRes
private fun getSideFpsOverlayAnimationForTransition(
@BiometricState oldState: Int,
@BiometricState newState: Int,
@@ -357,8 +326,4 @@ open class AuthBiometricFingerprintIconController(
)
}
}
-
- override fun onFoldUpdated(isFolded: Boolean) {
- isDeviceFolded = isFolded
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e04dd0680060..f330c34bfaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -124,6 +124,7 @@ public abstract class AuthBiometricView extends LinearLayout {
protected final int mTextColorHint;
private AuthPanelController mPanelController;
+
private PromptInfo mPromptInfo;
private boolean mRequireConfirmation;
private int mUserId;
@@ -266,11 +267,9 @@ public abstract class AuthBiometricView extends LinearLayout {
/** Create the controller for managing the icons transitions during the prompt.*/
@NonNull
protected abstract AuthIconController createIconController();
-
void setPanelController(AuthPanelController panelController) {
mPanelController = panelController;
}
-
void setPromptInfo(PromptInfo promptInfo) {
mPromptInfo = promptInfo;
}
@@ -463,9 +462,16 @@ public abstract class AuthBiometricView extends LinearLayout {
return false;
}
+ /**
+ * Updates mIconView animation on updates to fold state, device rotation, or rear display mode
+ * @param animation new asset to use for iconw
+ */
+ public void updateIconViewAnimation(int animation) {
+ mIconView.setAnimation(animation);
+ }
+
public void updateState(@BiometricState int newState) {
Log.v(TAG, "newState: " + newState);
-
mIconController.updateState(mState, newState);
switch (newState) {
@@ -625,7 +631,6 @@ public abstract class AuthBiometricView extends LinearLayout {
public void restoreState(@Nullable Bundle savedState) {
mSavedState = savedState;
}
-
private void setTextOrHide(TextView view, CharSequence charSequence) {
if (TextUtils.isEmpty(charSequence)) {
view.setVisibility(View.GONE);
@@ -658,7 +663,6 @@ public abstract class AuthBiometricView extends LinearLayout {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mIconController.onConfigurationChanged(newConfig);
if (mSavedState != null) {
updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index aeebb010eb1e..b386bc9d050b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -67,6 +67,8 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
import com.android.systemui.biometrics.ui.CredentialView;
+import com.android.systemui.biometrics.ui.binder.AuthBiometricFingerprintViewBinder;
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -126,6 +128,8 @@ public class AuthContainerView extends LinearLayout
// TODO: these should be migrated out once ready
private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
+ private final Provider<AuthBiometricFingerprintViewModel>
+ mAuthBiometricFingerprintViewModelProvider;
private final Provider<CredentialViewModel> mCredentialViewModelProvider;
@VisibleForTesting final BiometricCallback mBiometricCallback;
@@ -241,12 +245,14 @@ public class AuthContainerView extends LinearLayout
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
@NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+ @NonNull Provider<AuthBiometricFingerprintViewModel>
+ authBiometricFingerprintViewModelProvider,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider) {
mConfig.mSensorIds = sensorIds;
return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle,
panelInteractionDetector, userManager, lockPatternUtils, jankMonitor,
- biometricPromptInteractor, credentialViewModelProvider,
- new Handler(Looper.getMainLooper()), bgExecutor);
+ biometricPromptInteractor, authBiometricFingerprintViewModelProvider,
+ credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor);
}
}
@@ -339,6 +345,8 @@ public class AuthContainerView extends LinearLayout
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
@NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+ @NonNull Provider<AuthBiometricFingerprintViewModel>
+ authBiometricFingerprintViewModelProvider,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull Handler mainHandler,
@NonNull @Background DelayableExecutor bgExecutor) {
@@ -368,6 +376,7 @@ public class AuthContainerView extends LinearLayout
mBackgroundExecutor = bgExecutor;
mInteractionJankMonitor = jankMonitor;
mBiometricPromptInteractor = biometricPromptInteractor;
+ mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
// Inflate biometric view only if necessary.
@@ -386,6 +395,9 @@ public class AuthContainerView extends LinearLayout
fingerprintAndFaceView.updateOverrideIconLayoutParamsSize();
fingerprintAndFaceView.setFaceClass3(
faceProperties.sensorStrength == STRENGTH_STRONG);
+ final AuthBiometricFingerprintViewModel fpAndFaceViewModel =
+ mAuthBiometricFingerprintViewModelProvider.get();
+ AuthBiometricFingerprintViewBinder.bind(fingerprintAndFaceView, fpAndFaceViewModel);
mBiometricView = fingerprintAndFaceView;
} else if (fpProperties != null) {
final AuthBiometricFingerprintView fpView =
@@ -394,6 +406,9 @@ public class AuthContainerView extends LinearLayout
fpView.setSensorProperties(fpProperties);
fpView.setScaleFactorProvider(config.mScaleProvider);
fpView.updateOverrideIconLayoutParamsSize();
+ final AuthBiometricFingerprintViewModel fpViewModel =
+ mAuthBiometricFingerprintViewModelProvider.get();
+ AuthBiometricFingerprintViewBinder.bind(fpView, fpViewModel);
mBiometricView = fpView;
} else if (faceProperties != null) {
mBiometricView = (AuthBiometricFaceView) layoutInflater.inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 6eb3c708e9b5..3579e8cc0218 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -73,6 +73,7 @@ import com.android.settingslib.udfps.UdfpsUtils;
import com.android.systemui.CoreStartable;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -127,6 +128,9 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
// TODO: these should be migrated out once ready
@NonNull private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
+
+ @NonNull private final Provider<AuthBiometricFingerprintViewModel>
+ mAuthBiometricFingerprintViewModelProvider;
@NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider;
@NonNull private final LogContextInteractor mLogContextInteractor;
@@ -722,7 +726,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
}
onDialogDismissed(reason);
}
-
@Inject
public AuthController(Context context,
Execution execution,
@@ -741,6 +744,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
@NonNull UdfpsLogger udfpsLogger,
@NonNull LogContextInteractor logContextInteractor,
@NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+ @NonNull Provider<AuthBiometricFingerprintViewModel>
+ authBiometricFingerprintViewModelProvider,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull InteractionJankMonitor jankMonitor,
@Main Handler handler,
@@ -771,6 +776,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
mLogContextInteractor = logContextInteractor;
mBiometricPromptInteractor = biometricPromptInteractor;
+ mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
mOrientationListener = new BiometricDisplayListener(
@@ -1299,7 +1305,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
.build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle,
panelInteractionDetector, userManager, lockPatternUtils,
mInteractionJankMonitor, mBiometricPromptInteractor,
- mCredentialViewModelProvider);
+ mAuthBiometricFingerprintViewModelProvider, mCredentialViewModelProvider);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
index d6ad4da04dbe..f5f46405660f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
@@ -18,7 +18,6 @@ package com.android.systemui.biometrics
import android.annotation.DrawableRes
import android.content.Context
-import android.content.res.Configuration
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
@@ -94,8 +93,6 @@ abstract class AuthIconController(
/** Called during [onAnimationEnd] if the controller is not [deactivated]. */
open fun handleAnimationEnd(drawable: Drawable) {}
- open fun onConfigurationChanged(newConfig: Configuration) {}
-
// TODO(b/251476085): Migrate this to an extension at the appropriate level?
/** Load the given [rawResources] immediately so they are cached for use in the [context]. */
protected fun cacheLottieAssetsInContext(context: Context, vararg rawResources: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 4319f01bed14..962140fa2cff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -55,6 +55,7 @@ import com.airbnb.lottie.model.KeyPath
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -84,6 +85,7 @@ constructor(
private val activityTaskManager: ActivityTaskManager,
overviewProxyService: OverviewProxyService,
displayManager: DisplayManager,
+ private val displayStateInteractor: DisplayStateInteractor,
@Main private val mainExecutor: DelayableExecutor,
@Main private val handler: Handler,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -203,14 +205,16 @@ constructor(
request: SideFpsUiRequestSource,
@BiometricOverlayConstants.ShowReason reason: Int = BiometricOverlayConstants.REASON_UNKNOWN
) {
- requests.add(request)
- mainExecutor.execute {
- if (overlayView == null) {
- traceSection("SideFpsController#show(request=${request.name}, reason=$reason") {
- createOverlayForDisplay(reason)
+ if (!displayStateInteractor.isInRearDisplayMode.value) {
+ requests.add(request)
+ mainExecutor.execute {
+ if (overlayView == null) {
+ traceSection("SideFpsController#show(request=${request.name}, reason=$reason") {
+ createOverlayForDisplay(reason)
+ }
+ } else {
+ Log.v(TAG, "overlay already shown")
}
- } else {
- Log.v(TAG, "overlay already shown")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index f0b9f670f1e0..c83166385ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -21,8 +21,12 @@ import com.android.systemui.biometrics.data.repository.FingerprintPropertyReposi
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
import com.android.systemui.biometrics.data.repository.PromptRepository
import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor
import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl
import com.android.systemui.dagger.SysUISingleton
@@ -45,6 +49,9 @@ interface BiometricsModule {
@SysUISingleton
fun fingerprintRepository(impl: FingerprintPropertyRepositoryImpl):
FingerprintPropertyRepository
+ @Binds
+ @SysUISingleton
+ fun rearDisplayStateRepository(impl: RearDisplayStateRepositoryImpl): RearDisplayStateRepository
@Binds
@SysUISingleton
@@ -52,6 +59,10 @@ interface BiometricsModule {
@Binds
@SysUISingleton
+ fun providesDisplayStateInteractor(impl: DisplayStateInteractorImpl): DisplayStateInteractor
+
+ @Binds
+ @SysUISingleton
fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
new file mode 100644
index 000000000000..d17d961b5279
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.biometrics.data.repository
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import com.android.internal.util.ArrayUtils
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Provide current rear display state. */
+interface RearDisplayStateRepository {
+ /** Provides the current rear display state. */
+ val isInRearDisplayMode: StateFlow<Boolean>
+}
+
+@SysUISingleton
+class RearDisplayStateRepositoryImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ @Application context: Context,
+ deviceStateManager: DeviceStateManager,
+ @Main mainExecutor: Executor
+) : RearDisplayStateRepository {
+ override val isInRearDisplayMode: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val sendRearDisplayStateUpdate = { state: Boolean ->
+ trySendWithFailureLogging(
+ state,
+ TAG,
+ "Error sending rear display state update to $state"
+ )
+ }
+
+ val callback =
+ DeviceStateManager.DeviceStateCallback { state ->
+ val isInRearDisplayMode =
+ ArrayUtils.contains(
+ context.resources.getIntArray(
+ com.android.internal.R.array.config_rearDisplayDeviceStates
+ ),
+ state
+ )
+ sendRearDisplayStateUpdate(isInRearDisplayMode)
+ }
+
+ sendRearDisplayStateUpdate(false)
+ deviceStateManager.registerCallback(mainExecutor, callback)
+ awaitClose { deviceStateManager.unregisterCallback(callback) }
+ }
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ companion object {
+ const val TAG = "RearDisplayStateRepositoryImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
new file mode 100644
index 000000000000..c935aa290e21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.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.biometrics.domain.interactor
+
+import android.content.Context
+import android.content.res.Configuration
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.unfold.updates.FoldProvider
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Aggregates display state information. */
+interface DisplayStateInteractor {
+
+ /** Whether the device is currently in rear display mode. */
+ val isInRearDisplayMode: StateFlow<Boolean>
+
+ /** Whether the device is currently folded. */
+ val isFolded: Flow<Boolean>
+
+ /** Called on configuration changes, used to keep the display state in sync */
+ fun onConfigurationChanged(newConfig: Configuration)
+}
+
+/** Encapsulates logic for interacting with the display state. */
+class DisplayStateInteractorImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ @Application context: Context,
+ @Main mainExecutor: Executor,
+ rearDisplayStateRepository: RearDisplayStateRepository,
+) : DisplayStateInteractor {
+ private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
+
+ fun setScreenSizeFoldProvider(foldProvider: ScreenSizeFoldProvider) {
+ screenSizeFoldProvider = foldProvider
+ }
+
+ override val isFolded: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val sendFoldStateUpdate = { state: Boolean ->
+ trySendWithFailureLogging(
+ state,
+ TAG,
+ "Error sending fold state update to $state"
+ )
+ }
+
+ val callback =
+ object : FoldProvider.FoldCallback {
+ override fun onFoldUpdated(isFolded: Boolean) {
+ sendFoldStateUpdate(isFolded)
+ }
+ }
+ sendFoldStateUpdate(false)
+ screenSizeFoldProvider.registerCallback(callback, mainExecutor)
+ awaitClose { screenSizeFoldProvider.unregisterCallback(callback) }
+ }
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ override val isInRearDisplayMode: StateFlow<Boolean> =
+ rearDisplayStateRepository.isInRearDisplayMode
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ screenSizeFoldProvider.onConfigurationChange(newConfig)
+ }
+
+ companion object {
+ private const val TAG = "DisplayStateInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt
new file mode 100644
index 000000000000..e776ab44ee42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.binder
+
+import android.view.Surface
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.biometrics.AuthBiometricFingerprintView
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.launch
+
+object AuthBiometricFingerprintViewBinder {
+
+ /** Binds a [AuthBiometricFingerprintView] to a [AuthBiometricFingerprintViewModel]. */
+ @JvmStatic
+ fun bind(view: AuthBiometricFingerprintView, viewModel: AuthBiometricFingerprintViewModel) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.onConfigurationChanged(view.context.resources.configuration)
+ viewModel.setRotation(view.context.display?.orientation ?: Surface.ROTATION_0)
+ launch {
+ viewModel.iconAsset.collect { iconAsset ->
+ view.updateIconViewAnimation(iconAsset)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt
new file mode 100644
index 000000000000..617d80cee09d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.annotation.RawRes
+import android.content.res.Configuration
+import android.view.Surface
+import com.android.systemui.R
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/** Models UI of AuthBiometricFingerprintView to support rear display state changes. */
+class AuthBiometricFingerprintViewModel
+@Inject
+constructor(private val interactor: DisplayStateInteractor) {
+ /** Current device rotation. */
+ private var rotation: Int = Surface.ROTATION_0
+
+ /** Current AuthBiometricFingerprintView asset. */
+ val iconAsset: Flow<Int> =
+ combine(interactor.isFolded, interactor.isInRearDisplayMode) {
+ isFolded: Boolean,
+ isInRearDisplayMode: Boolean ->
+ getSideFpsAnimationAsset(isFolded, isInRearDisplayMode)
+ }
+
+ @RawRes
+ private fun getSideFpsAnimationAsset(
+ isDeviceFolded: Boolean,
+ isInRearDisplayMode: Boolean,
+ ): Int =
+ when (rotation) {
+ Surface.ROTATION_90 ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_portrait_reverse_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_topleft
+ } else {
+ R.raw.biometricprompt_portrait_base_topleft
+ }
+ Surface.ROTATION_270 ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_portrait_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_bottomright
+ } else {
+ R.raw.biometricprompt_portrait_base_bottomright
+ }
+ else ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_landscape_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_default
+ } else {
+ R.raw.biometricprompt_landscape_base
+ }
+ }
+
+ /** Called on configuration changes */
+ fun onConfigurationChanged(newConfig: Configuration) {
+ interactor.onConfigurationChanged(newConfig)
+ }
+
+ fun setRotation(newRotation: Int) {
+ rotation = newRotation
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2b72b2582f1a..871e66c3f917 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -61,6 +61,9 @@ object Flags {
// TODO(b/254512538): Tracking Bug
val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply")
+ // TODO(b/279735475): Tracking Bug
+ @JvmField val NEW_LIGHT_BAR_LOGIC = unreleasedFlag(279735475, "new_light_bar_logic")
+
/**
* This flag is server-controlled and should stay as [unreleasedFlag] since we never want to
* enable it on release builds.
@@ -203,11 +206,6 @@ object Flags {
)
/** Whether to inflate the bouncer view on a background thread. */
- // TODO(b/272091103): Tracking Bug
- @JvmField
- val ASYNC_INFLATE_BOUNCER = releasedFlag(229, "async_inflate_bouncer")
-
- /** Whether to inflate the bouncer view on a background thread. */
// TODO(b/273341787): Tracking Bug
@JvmField
val PREVENT_BYPASS_KEYGUARD = releasedFlag(230, "prevent_bypass_keyguard")
@@ -271,7 +269,7 @@ object Flags {
)
@JvmField
- val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = false)
+ val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = true)
// TODO(b/278068252): Tracking Bug
@JvmField
@@ -388,7 +386,7 @@ object Flags {
val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE = releasedFlag(912, "media_ttt_dismiss_gesture")
// TODO(b/266157412): Tracking Bug
- val MEDIA_RETAIN_SESSIONS = releasedFlag(913, "media_retain_sessions")
+ val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
// TODO(b/266739309): Tracking Bug
@JvmField
@@ -398,10 +396,10 @@ object Flags {
val MEDIA_RESUME_PROGRESS = releasedFlag(915, "media_resume_progress")
// TODO(b/267166152) : Tracking Bug
- val MEDIA_RETAIN_RECOMMENDATIONS = releasedFlag(916, "media_retain_recommendations")
+ val MEDIA_RETAIN_RECOMMENDATIONS = unreleasedFlag(916, "media_retain_recommendations")
// TODO(b/270437894): Tracking Bug
- val MEDIA_REMOTE_RESUME = releasedFlag(917, "media_remote_resume")
+ val MEDIA_REMOTE_RESUME = unreleasedFlag(917, "media_remote_resume")
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index d3b6fc237084..f64ed6078ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -128,6 +128,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -240,6 +241,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final ScreenshotHelper mScreenshotHelper;
private final SysuiColorExtractor mSysuiColorExtractor;
private final IStatusBarService mStatusBarService;
+ protected final LightBarController mLightBarController;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
@@ -349,6 +351,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
MetricsLogger metricsLogger,
SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
+ LightBarController lightBarController,
NotificationShadeWindowController notificationShadeWindowController,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
@@ -381,6 +384,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger = uiEventLogger;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
+ mLightBarController = lightBarController;
mNotificationShadeWindowController = notificationShadeWindowController;
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
@@ -694,6 +698,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
ActionsDialogLite dialog = new ActionsDialogLite(mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService,
+ mLightBarController,
mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing,
mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional, mKeyguardUpdateMonitor,
mLockPatternUtils);
@@ -2192,6 +2197,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected final SysuiColorExtractor mColorExtractor;
private boolean mKeyguardShowing;
protected float mScrimAlpha;
+ protected final LightBarController mLightBarController;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
@@ -2267,6 +2273,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
+ LightBarController lightBarController,
NotificationShadeWindowController notificationShadeWindowController,
Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
@@ -2282,6 +2289,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mPowerOptionsAdapter = powerAdapter;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
+ mLightBarController = lightBarController;
mNotificationShadeWindowController = notificationShadeWindowController;
mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
@@ -2474,6 +2482,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override
protected void start() {
mGlobalActionsLayout.updateList();
+ mLightBarController.setGlobalActionsVisible(true);
if (mBackgroundDrawable instanceof ScrimDrawable) {
mColorExtractor.addOnColorsChangedListener(this);
@@ -2504,6 +2513,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override
protected void stop() {
+ mLightBarController.setGlobalActionsVisible(false);
mColorExtractor.removeOnColorsChangedListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index b92499eb883e..b7ba2019777e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -56,11 +56,11 @@ public class WorkLockActivityController {
tscl.registerTaskStackListener(mLockListener);
}
- private void startWorkChallengeInTask(ActivityManager.RunningTaskInfo info) {
+ private void startWorkChallengeInTask(ActivityManager.RunningTaskInfo info, int userId) {
String packageName = info.baseActivity != null ? info.baseActivity.getPackageName() : "";
Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
.setComponent(new ComponentName(mContext, WorkLockActivity.class))
- .putExtra(Intent.EXTRA_USER_ID, info.userId)
+ .putExtra(Intent.EXTRA_USER_ID, userId)
.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -76,10 +76,11 @@ public class WorkLockActivityController {
} else {
// Starting the activity inside the task failed. We can't be sure why, so to be
// safe just remove the whole task if it still exists.
+ Log.w(TAG, "Failed to start work lock activity, will remove task=" + info.taskId);
try {
mIatm.removeTask(info.taskId);
} catch (RemoteException e) {
- Log.w(TAG, "Failed to get description for task=" + info.taskId);
+ Log.e(TAG, "Failed to remove task=" + info.taskId);
}
}
}
@@ -112,8 +113,8 @@ public class WorkLockActivityController {
private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() {
@Override
- public void onTaskProfileLocked(ActivityManager.RunningTaskInfo info) {
- startWorkChallengeInTask(info);
+ public void onTaskProfileLocked(ActivityManager.RunningTaskInfo info, int userId) {
+ startWorkChallengeInTask(info, userId);
}
};
}
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 a98a7d8f8639..951f0bd76a64 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
@@ -21,6 +21,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.os.IBinder
@@ -33,6 +34,7 @@ import android.widget.FrameLayout
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
import com.android.systemui.R
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -61,6 +63,7 @@ constructor(
private val clockRegistry: ClockRegistry,
private val broadcastDispatcher: BroadcastDispatcher,
private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
@Assisted bundle: Bundle,
) {
@@ -112,7 +115,9 @@ constructor(
setUpBottomArea(rootView)
- setupSmartspace(rootView)
+ setUpSmartspace(rootView)
+
+ setUpUdfps(rootView)
setUpClock(rootView)
@@ -169,49 +174,53 @@ constructor(
/**
* This sets up and shows a non-interactive smart space
*
- * The top padding is as follows:
- * Status bar height + clock top margin + keyguard smart space top offset
+ * The top padding is as follows: Status bar height + clock top margin + keyguard smart space
+ * top offset
*
- * The start padding is as follows:
- * Clock padding start + Below clock padding start
+ * The start padding is as follows: Clock padding start + Below clock padding start
*
- * The end padding is as follows:
- * Below clock padding end
+ * The end padding is as follows: Below clock padding end
*/
- private fun setupSmartspace(parentView: ViewGroup) {
- if (!lockscreenSmartspaceController.isEnabled() ||
- !lockscreenSmartspaceController.isDateWeatherDecoupled()) {
+ private fun setUpSmartspace(parentView: ViewGroup) {
+ if (
+ !lockscreenSmartspaceController.isEnabled() ||
+ !lockscreenSmartspaceController.isDateWeatherDecoupled()
+ ) {
return
}
smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
- val topPadding: Int = with(context.resources) {
- getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) +
+ val topPadding: Int =
+ with(context.resources) {
+ getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) +
getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
- }
+ }
- val startPadding: Int = with(context.resources) {
- getDimensionPixelSize(R.dimen.clock_padding_start) +
+ val startPadding: Int =
+ with(context.resources) {
+ getDimensionPixelSize(R.dimen.clock_padding_start) +
getDimensionPixelSize(R.dimen.below_clock_padding_start)
- }
+ }
- val endPadding: Int = context.resources
- .getDimensionPixelSize(R.dimen.below_clock_padding_end)
+ val endPadding: Int =
+ context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end)
smartSpaceView?.let {
it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
it.isClickable = false
parentView.addView(
- it,
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- ),
+ it,
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ ),
)
}
+
+ smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
}
private fun setUpBottomArea(parentView: ViewGroup) {
@@ -234,6 +243,33 @@ constructor(
)
}
+ private fun setUpUdfps(parentView: ViewGroup) {
+ val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds
+
+ // If sensorBounds are default rect, then there is no UDFPS
+ if (sensorBounds == Rect()) {
+ return
+ }
+
+ // Place the UDFPS view in the proper sensor location
+ val fingerprintLayoutParams =
+ FrameLayout.LayoutParams(sensorBounds.width(), sensorBounds.height())
+ fingerprintLayoutParams.setMarginsRelative(
+ sensorBounds.left,
+ sensorBounds.top,
+ sensorBounds.right,
+ sensorBounds.bottom
+ )
+ val finger =
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.udfps_keyguard_view_internal,
+ parentView,
+ false,
+ ) as View
+ parentView.addView(finger, fingerprintLayoutParams)
+ }
+
private fun setUpClock(parentView: ViewGroup) {
val clockChangeListener =
object : ClockRegistry.ClockChangeListener {
@@ -266,8 +302,6 @@ constructor(
disposables.add(DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) })
onClockChanged(parentView)
-
- updateSmartspaceWithSetupClock()
}
private fun onClockChanged(parentView: ViewGroup) {
@@ -281,33 +315,22 @@ constructor(
?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
clockView?.let { parentView.removeView(it) }
- clockView = largeClock?.view?.apply {
- if (shouldHighlightSelectedAffordance) {
- alpha = DIM_ALPHA
+ clockView =
+ largeClock?.view?.apply {
+ if (shouldHighlightSelectedAffordance) {
+ alpha = DIM_ALPHA
+ }
+ parentView.addView(this)
+ visibility = View.VISIBLE
}
- parentView.addView(this)
- visibility = View.VISIBLE
- }
} else {
clockView?.visibility = View.GONE
}
- }
- /**
- * Updates smart space after clock is set up. Used to show or hide smartspace with the right
- * opacity based on the clock after setup.
- */
- private fun updateSmartspaceWithSetupClock() {
+ // Hide smart space if the clock has weather display; otherwise show it
val hasCustomWeatherDataDisplay =
- clockController
- .clock
- ?.largeClock
- ?.config
- ?.hasCustomWeatherDataDisplay == true
-
+ clockController.clock?.largeClock?.config?.hasCustomWeatherDataDisplay == true
hideSmartspace(hasCustomWeatherDataDisplay)
-
- smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 9997730fa938..fa4211487d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -43,7 +43,6 @@ import android.media.session.PlaybackState
import android.net.Uri
import android.os.Parcelable
import android.os.Process
-import android.os.RemoteException
import android.os.UserHandle
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -53,7 +52,6 @@ import android.util.Log
import android.util.Pair as APair
import androidx.media.utils.MediaConstants
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Dumpable
import com.android.systemui.R
@@ -139,8 +137,6 @@ internal val EMPTY_SMARTSPACE_MEDIA_DATA =
expiryTimeMs = 0,
)
-const val MEDIA_TITLE_ERROR_MESSAGE = "Invalid media data: title is null or blank."
-
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.isMediaNotification()
}
@@ -185,7 +181,6 @@ class MediaDataManager(
private val logger: MediaUiEventLogger,
private val smartspaceManager: SmartspaceManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val statusBarService: IStatusBarService,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -257,7 +252,6 @@ class MediaDataManager(
mediaFlags: MediaFlags,
logger: MediaUiEventLogger,
smartspaceManager: SmartspaceManager,
- statusBarService: IStatusBarService,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : this(
context,
@@ -283,7 +277,6 @@ class MediaDataManager(
logger,
smartspaceManager,
keyguardUpdateMonitor,
- statusBarService,
)
private val appChangeReceiver =
@@ -385,21 +378,21 @@ class MediaDataManager(
fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
if (useQsMediaPlayer && isMediaNotification(sbn)) {
- var isNewlyActiveEntry = false
+ var logEvent = false
Assert.isMainThread()
val oldKey = findExistingEntry(key, sbn.packageName)
if (oldKey == null) {
val instanceId = logger.getNewInstanceId()
val temp = LOADING.copy(packageName = sbn.packageName, instanceId = instanceId)
mediaEntries.put(key, temp)
- isNewlyActiveEntry = true
+ logEvent = true
} else if (oldKey != key) {
// Resume -> active conversion; move to new key
val oldData = mediaEntries.remove(oldKey)!!
- isNewlyActiveEntry = true
+ logEvent = true
mediaEntries.put(key, oldData)
}
- loadMediaData(key, sbn, oldKey, isNewlyActiveEntry)
+ loadMediaData(key, sbn, oldKey, logEvent)
} else {
onNotificationRemoved(key)
}
@@ -482,9 +475,9 @@ class MediaDataManager(
key: String,
sbn: StatusBarNotification,
oldKey: String?,
- isNewlyActiveEntry: Boolean = false,
+ logEvent: Boolean = false
) {
- backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
+ backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, logEvent) }
}
/** Add a listener for changes in this class */
@@ -608,11 +601,9 @@ class MediaDataManager(
}
}
- private fun removeEntry(key: String, logEvent: Boolean = true) {
+ private fun removeEntry(key: String) {
mediaEntries.remove(key)?.let {
- if (logEvent) {
- logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
- }
+ logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
}
notifyMediaDataRemoved(key)
}
@@ -760,7 +751,7 @@ class MediaDataManager(
key: String,
sbn: StatusBarNotification,
oldKey: String?,
- isNewlyActiveEntry: Boolean = false,
+ logEvent: Boolean = false
) {
val token =
sbn.notification.extras.getParcelable(
@@ -774,34 +765,6 @@ class MediaDataManager(
val metadata = mediaController.metadata
val notif: Notification = sbn.notification
- // Song name
- var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
- if (song == null) {
- song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
- }
- if (song == null) {
- song = HybridGroupManager.resolveTitle(notif)
- }
- // Media data must have a title.
- if (song.isNullOrBlank()) {
- try {
- statusBarService.onNotificationError(
- sbn.packageName,
- sbn.tag,
- sbn.id,
- sbn.uid,
- sbn.initialPid,
- MEDIA_TITLE_ERROR_MESSAGE,
- sbn.user.identifier
- )
- } catch (e: RemoteException) {
- Log.e(TAG, "cancelNotification failed: $e")
- }
- // Only add log for media removed if active media is updated with invalid title.
- foregroundExecutor.execute { removeEntry(key, !isNewlyActiveEntry) }
- return
- }
-
val appInfo =
notif.extras.getParcelable(
Notification.EXTRA_BUILDER_APPLICATION_INFO,
@@ -830,6 +793,15 @@ class MediaDataManager(
// App Icon
val smallIcon = sbn.notification.smallIcon
+ // Song name
+ var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
+ if (song == null) {
+ song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
+ }
+ if (song == null) {
+ song = HybridGroupManager.resolveTitle(notif)
+ }
+
// Explicit Indicator
var isExplicit = false
if (mediaFlags.isExplicitIndicatorEnabled()) {
@@ -901,7 +873,7 @@ class MediaDataManager(
val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
val appUid = appInfo?.uid ?: Process.INVALID_UID
- if (isNewlyActiveEntry) {
+ if (logEvent) {
logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
} else if (playbackLocation != currentEntry?.playbackLocation) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt
new file mode 100644
index 000000000000..c209a000deb4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notetask
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import javax.inject.Inject
+
+/**
+ * An internal proxy activity that starts the notes role setting.
+ *
+ * This activity is introduced mainly for the error handling of the notes app lock screen shortcut
+ * picker, which only supports package + action but not extras. See
+ * [KeyguardQuickAffordanceConfig.PickerScreenState.Disabled.actionComponentName].
+ */
+class LaunchNotesRoleSettingsTrampolineActivity
+@Inject
+constructor(
+ private val controller: NoteTaskController,
+) : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val entryPoint =
+ if (intent?.action == ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) {
+ NoteTaskEntryPoint.QUICK_AFFORDANCE
+ } else {
+ null
+ }
+ controller.startNotesRoleSetting(this, entryPoint)
+ finish()
+ }
+
+ companion object {
+ const val ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE =
+ "com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index d4052f54b3da..7e9b346c9577 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -31,15 +31,14 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon
-import android.os.Build
import android.os.UserHandle
import android.os.UserManager
-import android.util.Log
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity
@@ -92,10 +91,10 @@ constructor(
if (info.launchMode != NoteTaskLaunchMode.AppBubble) return
if (isExpanding) {
- logDebug { "onBubbleExpandChanged - expanding: $info" }
+ debugLog { "onBubbleExpandChanged - expanding: $info" }
eventLogger.logNoteTaskOpened(info)
} else {
- logDebug { "onBubbleExpandChanged - collapsing: $info" }
+ debugLog { "onBubbleExpandChanged - collapsing: $info" }
eventLogger.logNoteTaskClosed(info)
}
}
@@ -112,6 +111,43 @@ constructor(
)
}
+ /** Starts the notes role setting. */
+ fun startNotesRoleSetting(activityContext: Context, entryPoint: NoteTaskEntryPoint?) {
+ val user =
+ if (entryPoint == null) {
+ userTracker.userHandle
+ } else {
+ getUserForHandlingNotesTaking(entryPoint)
+ }
+ activityContext.startActivityAsUser(
+ Intent(Intent.ACTION_MANAGE_DEFAULT_APP).apply {
+ putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
+ },
+ user
+ )
+ }
+
+ /**
+ * Returns the [UserHandle] of an android user that should handle the notes taking [entryPoint].
+ *
+ * On company owned personally enabled (COPE) devices, if the given [entryPoint] is in the
+ * [FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES] list, the default notes app in the work
+ * profile user will always be launched.
+ *
+ * On non managed devices or devices with other management modes, the current [UserHandle] is
+ * returned.
+ */
+ fun getUserForHandlingNotesTaking(entryPoint: NoteTaskEntryPoint): UserHandle =
+ if (
+ entryPoint in FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES &&
+ devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile
+ ) {
+ userTracker.userProfiles.firstOrNull { userManager.isManagedProfile(it.id) }?.userHandle
+ ?: userTracker.userHandle
+ } else {
+ userTracker.userHandle
+ }
+
/**
* Shows a note task. How the task is shown will depend on when the method is invoked.
*
@@ -122,30 +158,13 @@ constructor(
* bubble is already opened.
*
* That will let users open other apps in full screen, and take contextual notes.
- *
- * On company owned personally enabled (COPE) devices, if the given [entryPoint] is in the
- * [FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES] list, the default notes app in the work
- * profile user will always be launched.
*/
fun showNoteTask(
entryPoint: NoteTaskEntryPoint,
) {
if (!isEnabled) return
- val user: UserHandle =
- if (
- entryPoint in FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES &&
- devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile
- ) {
- userTracker.userProfiles
- .firstOrNull { userManager.isManagedProfile(it.id) }
- ?.userHandle
- ?: userTracker.userHandle
- } else {
- userTracker.userHandle
- }
-
- showNoteTaskAsUser(entryPoint, user)
+ showNoteTaskAsUser(entryPoint, getUserForHandlingNotesTaking(entryPoint))
}
/** A variant of [showNoteTask] which launches note task in the given [user]. */
@@ -168,14 +187,14 @@ constructor(
isKeyguardLocked &&
devicePolicyManager.areKeyguardShortcutsDisabled(userId = user.identifier)
) {
- logDebug { "Enterprise policy disallows launching note app when the screen is locked." }
+ debugLog { "Enterprise policy disallows launching note app when the screen is locked." }
return
}
val info = resolver.resolveInfo(entryPoint, isKeyguardLocked, user)
if (info == null) {
- logDebug { "Default notes app isn't set" }
+ debugLog { "Default notes app isn't set" }
showNoDefaultNotesAppToast()
return
}
@@ -184,7 +203,7 @@ constructor(
try {
// TODO(b/266686199): We should handle when app not available. For now, we log.
- logDebug { "onShowNoteTask - start: $info on user#${user.identifier}" }
+ debugLog { "onShowNoteTask - start: $info on user#${user.identifier}" }
when (info.launchMode) {
is NoteTaskLaunchMode.AppBubble -> {
val intent = createNoteTaskIntent(info)
@@ -192,7 +211,7 @@ constructor(
Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
bubbles.showOrHideAppBubble(intent, user, icon)
// App bubble logging happens on `onBubbleExpandChanged`.
- logDebug { "onShowNoteTask - opened as app bubble: $info" }
+ debugLog { "onShowNoteTask - opened as app bubble: $info" }
}
is NoteTaskLaunchMode.Activity -> {
if (activityManager.isInForeground(info.packageName)) {
@@ -200,20 +219,20 @@ constructor(
val intent = createHomeIntent()
context.startActivityAsUser(intent, user)
eventLogger.logNoteTaskClosed(info)
- logDebug { "onShowNoteTask - closed as activity: $info" }
+ debugLog { "onShowNoteTask - closed as activity: $info" }
} else {
val intent = createNoteTaskIntent(info)
context.startActivityAsUser(intent, user)
eventLogger.logNoteTaskOpened(info)
- logDebug { "onShowNoteTask - opened as activity: $info" }
+ debugLog { "onShowNoteTask - opened as activity: $info" }
}
}
}
- logDebug { "onShowNoteTask - success: $info" }
+ debugLog { "onShowNoteTask - success: $info" }
} catch (e: ActivityNotFoundException) {
- logDebug { "onShowNoteTask - failed: $info" }
+ debugLog { "onShowNoteTask - failed: $info" }
}
- logDebug { "onShowNoteTask - completed: $info" }
+ debugLog { "onShowNoteTask - completed: $info" }
}
@VisibleForTesting
@@ -253,7 +272,7 @@ constructor(
PackageManager.DONT_KILL_APP,
)
- logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" }
+ debugLog { "setNoteTaskShortcutEnabled - completed: $isEnabled" }
}
/**
@@ -352,11 +371,6 @@ private fun createNoteTaskIntent(info: NoteTaskInfo): Intent =
}
}
-/** [Log.println] a [Log.DEBUG] message, only when [Build.IS_DEBUGGABLE]. */
-private inline fun Any.logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName.orEmpty(), message())
-}
-
/** Creates an [Intent] which forces the current app to background by calling home. */
private fun createHomeIntent(): Intent =
Intent(Intent.ACTION_MAIN).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 2c62ffd4841f..4d5173a1ec0a 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -45,6 +45,10 @@ interface NoteTaskModule {
@[Binds IntoMap ClassKey(LaunchNoteTaskManagedProfileProxyActivity::class)]
fun LaunchNoteTaskManagedProfileProxyActivity.bindNoteTaskLauncherProxyActivity(): Activity
+ @[Binds IntoMap ClassKey(LaunchNotesRoleSettingsTrampolineActivity::class)]
+ fun LaunchNotesRoleSettingsTrampolineActivity.bindLaunchNotesRoleSettingsTrampolineActivity():
+ Activity
+
companion object {
@[Provides NoteTaskEnabledKey]
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 2da5b7621723..444407c2341a 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -16,9 +16,12 @@
package com.android.systemui.notetask.quickaffordance
+import android.app.role.OnRoleHoldersChangedListener
+import android.app.role.RoleManager
import android.content.Context
import android.hardware.input.InputSettings
import android.os.Build
+import android.os.UserHandle
import android.os.UserManager
import android.util.Log
import com.android.keyguard.KeyguardUpdateMonitor
@@ -27,17 +30,22 @@ import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEnabledKey
-import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskInfoResolver
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR
import com.android.systemui.stylus.StylusManager
import dagger.Lazy
+import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
@@ -49,13 +57,16 @@ import kotlinx.coroutines.flow.onEach
class NoteTaskQuickAffordanceConfig
@Inject
constructor(
- context: Context,
+ private val context: Context,
private val controller: NoteTaskController,
+ private val noteTaskInfoResolver: NoteTaskInfoResolver,
private val stylusManager: StylusManager,
+ private val roleManager: RoleManager,
private val keyguardMonitor: KeyguardUpdateMonitor,
private val userManager: UserManager,
private val lazyRepository: Lazy<KeyguardQuickAffordanceRepository>,
@NoteTaskEnabledKey private val isEnabled: Boolean,
+ @Background private val backgroundExecutor: Executor,
) : KeyguardQuickAffordanceConfig {
override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
@@ -73,15 +84,24 @@ constructor(
val configSelectedFlow = repository.createConfigSelectedFlow(key)
val stylusEverUsedFlow = stylusManager.createStylusEverUsedFlow(context)
val userUnlockedFlow = userManager.createUserUnlockedFlow(keyguardMonitor)
- combine(userUnlockedFlow, stylusEverUsedFlow, configSelectedFlow) {
+ val defaultNotesAppFlow =
+ roleManager.createNotesRoleFlow(backgroundExecutor, controller, noteTaskInfoResolver)
+ combine(userUnlockedFlow, stylusEverUsedFlow, configSelectedFlow, defaultNotesAppFlow) {
isUserUnlocked,
isStylusEverUsed,
- isConfigSelected ->
+ isConfigSelected,
+ isDefaultNotesAppSet ->
logDebug { "lockScreenState:isUserUnlocked=$isUserUnlocked" }
logDebug { "lockScreenState:isStylusEverUsed=$isStylusEverUsed" }
logDebug { "lockScreenState:isConfigSelected=$isConfigSelected" }
+ logDebug { "lockScreenState:isDefaultNotesAppSet=$isDefaultNotesAppSet" }
- if (isEnabled && isUserUnlocked && (isConfigSelected || isStylusEverUsed)) {
+ if (
+ isEnabled &&
+ isUserUnlocked &&
+ isDefaultNotesAppSet &&
+ (isConfigSelected || isStylusEverUsed)
+ ) {
val contentDescription = ContentDescription.Resource(pickerNameResourceId)
val icon = Icon.Resource(pickerIconResourceId, contentDescription)
LockScreenState.Visible(icon)
@@ -92,15 +112,34 @@ constructor(
.onEach { state -> logDebug { "lockScreenState=$state" } }
}
- override suspend fun getPickerScreenState() =
- if (isEnabled) {
- PickerScreenState.Default()
- } else {
- PickerScreenState.UnavailableOnDevice
+ override suspend fun getPickerScreenState(): PickerScreenState {
+ val isDefaultNotesAppSet =
+ noteTaskInfoResolver.resolveInfo(
+ QUICK_AFFORDANCE,
+ user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ ) != null
+ return when {
+ isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default()
+ isEnabled -> {
+ PickerScreenState.Disabled(
+ listOf(
+ context.getString(
+ R.string.keyguard_affordance_enablement_dialog_notes_app_instruction
+ )
+ ),
+ context.getString(
+ R.string.keyguard_affordance_enablement_dialog_notes_app_action
+ ),
+ "${context.packageName}$COMPONENT_NAME_SEPARATOR" +
+ "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE",
+ )
+ }
+ else -> PickerScreenState.UnavailableOnDevice
}
+ }
override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
- controller.showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ controller.showNoteTask(entryPoint = QUICK_AFFORDANCE)
return OnTriggeredResult.Handled
}
}
@@ -129,6 +168,27 @@ private fun StylusManager.createStylusEverUsedFlow(context: Context) = callbackF
awaitClose { unregisterCallback(callback) }
}
+private fun RoleManager.createNotesRoleFlow(
+ executor: Executor,
+ noteTaskController: NoteTaskController,
+ noteTaskInfoResolver: NoteTaskInfoResolver,
+) = callbackFlow {
+ fun isDefaultNotesAppSetForUser() =
+ noteTaskInfoResolver.resolveInfo(
+ QUICK_AFFORDANCE,
+ user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ ) != null
+
+ trySendBlocking(isDefaultNotesAppSetForUser())
+ val callback = OnRoleHoldersChangedListener { roleName, _ ->
+ if (roleName == RoleManager.ROLE_NOTES) {
+ trySendBlocking(isDefaultNotesAppSetForUser())
+ }
+ }
+ addOnRoleHoldersChangedListenerAsUser(executor, callback, UserHandle.ALL)
+ awaitClose { removeOnRoleHoldersChangedListenerAsUser(callback, UserHandle.ALL) }
+}
+
private fun KeyguardQuickAffordanceRepository.createConfigSelectedFlow(key: String) =
selections.map { selected ->
selected.values.flatten().any { selectedConfig -> selectedConfig.key == key }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index 0f38d32e0b64..8ca13b9776bb 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -18,12 +18,11 @@ package com.android.systemui.notetask.shortcut
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.os.Bundle
import android.os.UserHandle
import android.os.UserManager
-import android.util.Log
import androidx.activity.ComponentActivity
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
import com.android.systemui.settings.UserTracker
@@ -68,7 +67,7 @@ constructor(
val mainUser: UserHandle? = userManager.mainUser
if (userManager.isManagedProfile) {
if (mainUser == null) {
- logDebug { "Can't find the main user. Skipping the notes app launch." }
+ debugLog { "Can't find the main user. Skipping the notes app launch." }
} else {
controller.startNoteTaskProxyActivityForUser(mainUser)
}
@@ -89,8 +88,3 @@ constructor(
}
}
}
-
-/** [Log.println] a [Log.DEBUG] message, only when [Build.IS_DEBUGGABLE]. */
-private inline fun Any.logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName.orEmpty(), message())
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 79167f276576..166ba9fba166 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -15,6 +15,7 @@
package com.android.systemui.privacy
import android.content.Context
+import android.content.res.Configuration
import android.util.AttributeSet
import android.view.Gravity.CENTER_VERTICAL
import android.view.Gravity.END
@@ -102,6 +103,11 @@ class OngoingPrivacyChip @JvmOverloads constructor(
R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
}
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ updateResources()
+ }
+
private fun updateResources() {
iconMargin = context.resources
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
@@ -110,8 +116,11 @@ class OngoingPrivacyChip @JvmOverloads constructor(
iconColor =
Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ val height = context.resources
+ .getDimensionPixelSize(R.dimen.ongoing_appops_chip_height)
val padding = context.resources
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+ iconsContainer.layoutParams.height = height
iconsContainer.setPaddingRelative(padding, 0, padding, 0)
iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index b7f9f6bc0e4a..1afc885cb70d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -38,7 +38,9 @@ import java.io.PrintWriter;
*/
public class QSContainerImpl extends FrameLayout implements Dumpable {
+ private int mFancyClippingLeftInset;
private int mFancyClippingTop;
+ private int mFancyClippingRightInset;
private int mFancyClippingBottom;
private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
private final Path mFancyClippingPath = new Path();
@@ -53,6 +55,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
private boolean mQsDisabled;
private int mContentHorizontalPadding = -1;
private boolean mClippingEnabled;
+ private boolean mIsFullWidth;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -237,7 +240,8 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
/**
* Clip QS bottom using a concave shape.
*/
- public void setFancyClipping(int top, int bottom, int radius, boolean enabled) {
+ public void setFancyClipping(int leftInset, int top, int rightInset, int bottom, int radius,
+ boolean enabled, boolean fullWidth) {
boolean updatePath = false;
if (mFancyClippingRadii[0] != radius) {
mFancyClippingRadii[0] = radius;
@@ -246,10 +250,18 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
mFancyClippingRadii[3] = radius;
updatePath = true;
}
+ if (mFancyClippingLeftInset != leftInset) {
+ mFancyClippingLeftInset = leftInset;
+ updatePath = true;
+ }
if (mFancyClippingTop != top) {
mFancyClippingTop = top;
updatePath = true;
}
+ if (mFancyClippingRightInset != rightInset) {
+ mFancyClippingRightInset = rightInset;
+ updatePath = true;
+ }
if (mFancyClippingBottom != bottom) {
mFancyClippingBottom = bottom;
updatePath = true;
@@ -258,6 +270,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
mClippingEnabled = enabled;
updatePath = true;
}
+ if (mIsFullWidth != fullWidth) {
+ mIsFullWidth = fullWidth;
+ updatePath = true;
+ }
if (updatePath) {
updateClippingPath();
@@ -281,15 +297,21 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
return;
}
- mFancyClippingPath.addRoundRect(0, mFancyClippingTop, getWidth(),
+ int clippingLeft = mIsFullWidth ? -mFancyClippingLeftInset : 0;
+ int clippingRight = mIsFullWidth ? getWidth() + mFancyClippingRightInset : getWidth();
+ mFancyClippingPath.addRoundRect(clippingLeft, mFancyClippingTop, clippingRight,
mFancyClippingBottom, mFancyClippingRadii, Path.Direction.CW);
invalidate();
}
@Override
public void dump(PrintWriter pw, String[] args) {
- pw.println(getClass().getSimpleName() + " updateClippingPath: top("
- + mFancyClippingTop + ") bottom(" + mFancyClippingBottom + ") mClippingEnabled("
- + mClippingEnabled + ")");
+ pw.println(getClass().getSimpleName() + " updateClippingPath: "
+ + "leftInset(" + mFancyClippingLeftInset + ") "
+ + "top(" + mFancyClippingTop + ") "
+ + "rightInset(" + mFancyClippingRightInset + ") "
+ + "bottom(" + mFancyClippingBottom + ") "
+ + "mClippingEnabled(" + mClippingEnabled + ") "
+ + "mIsFullWidth(" + mIsFullWidth + ")");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d806afa91dd1..fd3f70148a69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,9 +18,10 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.res.Configuration;
@@ -395,9 +396,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
@Override
- public void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible) {
+ public void setFancyClipping(int leftInset, int top, int rightInset, int bottom,
+ int cornerRadius, boolean visible, boolean fullWidth) {
if (getView() instanceof QSContainerImpl) {
- ((QSContainerImpl) getView()).setFancyClipping(top, bottom, cornerRadius, visible);
+ ((QSContainerImpl) getView()).setFancyClipping(leftInset, top, rightInset, bottom,
+ cornerRadius, visible, fullWidth);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index 0bce1f728e18..b70b94b00923 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -719,7 +719,8 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener {
String name = vpnName != null ? vpnName : vpnNameWorkProfile;
String namedVp = mDpm.getResources().getString(
QS_DIALOG_MANAGEMENT_NAMED_VPN,
- () -> mContext.getString(R.string.monitoring_description_named_vpn, name),
+ () -> mContext.getString(
+ R.string.monitoring_description_managed_device_named_vpn, name),
name);
message.append(namedVp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 4a3199850e0f..b80668379e49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -385,6 +385,11 @@ open class QSTileViewImpl @JvmOverloads constructor(
super.onInitializeAccessibilityNodeInfo(info)
// Clear selected state so it is not announce by talkback.
info.isSelected = false
+ info.text = if (TextUtils.isEmpty(secondaryLabel.text)) {
+ "${label.text}"
+ } else {
+ "${label.text}, ${secondaryLabel.text}"
+ }
if (lastDisabledByPolicy) {
info.addAction(
AccessibilityNodeInfo.AccessibilityAction(
@@ -402,12 +407,6 @@ open class QSTileViewImpl @JvmOverloads constructor(
accessibilityClass
}
if (Switch::class.java.name == accessibilityClass) {
- val label = resources.getString(
- if (tileState) R.string.switch_bar_on else R.string.switch_bar_off)
- // Set the text here for tests in
- // android.platform.test.scenario.sysui.quicksettings. Can be removed when
- // UiObject2 has a new getStateDescription() API and tests are updated.
- info.text = label
info.isChecked = tileState
info.isCheckable = true
if (isLongClickable) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 51179153fc73..137a99ef39b8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4474,7 +4474,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mDisplayTopInset = combinedInsets.top;
mDisplayRightInset = combinedInsets.right;
mDisplayLeftInset = combinedInsets.left;
- mQsController.setDisplayInsets(mDisplayRightInset, mDisplayLeftInset);
+ mQsController.setDisplayInsets(mDisplayLeftInset, mDisplayRightInset);
mNavigationBarBottomHeight = insets.getStableInsetBottom();
updateMaxHeadsUpTranslation();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 4012736b8beb..a1fa8fbf9075 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -85,11 +85,13 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LargeScreenUtils;
@@ -116,6 +118,7 @@ public class QuickSettingsController {
private final PulseExpansionHandler mPulseExpansionHandler;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final LightBarController mLightBarController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final NotificationShadeDepthController mDepthController;
@@ -135,6 +138,7 @@ public class QuickSettingsController {
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final ShadeLogger mShadeLog;
private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ private final CastController mCastController;
private final FeatureFlags mFeatureFlags;
private final InteractionJankMonitor mInteractionJankMonitor;
private final ShadeRepository mShadeRepository;
@@ -302,6 +306,7 @@ public class QuickSettingsController {
NotificationRemoteInputManager remoteInputManager,
ShadeExpansionStateManager shadeExpansionStateManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ LightBarController lightBarController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationShadeDepthController notificationShadeDepthController,
@@ -324,7 +329,8 @@ public class QuickSettingsController {
InteractionJankMonitor interactionJankMonitor,
ShadeLogger shadeLog,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
- ShadeRepository shadeRepository
+ ShadeRepository shadeRepository,
+ CastController castController
) {
mPanelViewControllerLazy = panelViewControllerLazy;
mPanelView = panelView;
@@ -343,6 +349,7 @@ public class QuickSettingsController {
mRemoteInputManager = remoteInputManager;
mShadeExpansionStateManager = shadeExpansionStateManager;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mLightBarController = lightBarController;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mDepthController = notificationShadeDepthController;
@@ -364,6 +371,7 @@ public class QuickSettingsController {
mMetricsLogger = metricsLogger;
mShadeLog = shadeLog;
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mCastController = castController;
mFeatureFlags = featureFlags;
mInteractionJankMonitor = interactionJankMonitor;
mShadeRepository = shadeRepository;
@@ -879,7 +887,9 @@ public class QuickSettingsController {
}
void setOverScrollAmount(int overExpansion) {
- mQs.setOverScrollAmount(overExpansion);
+ if (mQs != null) {
+ mQs.setOverScrollAmount(overExpansion);
+ }
}
private void setOverScrolling(boolean overscrolling) {
@@ -1014,6 +1024,9 @@ public class QuickSettingsController {
mShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mShadeHeaderController.setQsVisible(mVisible);
+
+ // Update the light bar
+ mLightBarController.setQsExpanded(mFullyExpanded);
}
float getLockscreenShadeDragProgress() {
@@ -1188,7 +1201,9 @@ public class QuickSettingsController {
mLastClipBounds.set(left, top, right, bottom);
if (mIsFullWidth) {
clipStatusView = qsVisible;
- float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
+ float screenCornerRadius =
+ mRecordingController.isRecording() || mCastController.hasConnectedCastDevice()
+ ? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
mScrimController.setNotificationBottomRadius(radius);
@@ -1217,10 +1232,13 @@ public class QuickSettingsController {
mVisible = qsVisible;
mQs.setQsVisible(qsVisible);
mQs.setFancyClipping(
+ mDisplayLeftInset,
clipTop,
+ mDisplayRightInset,
clipBottom,
radius,
- qsVisible && !mSplitShadeEnabled);
+ qsVisible && !mSplitShadeEnabled,
+ mIsFullWidth);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 07b686948d7f..b6970aef6cad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -20,6 +20,7 @@ import android.app.ActivityManager
import android.content.res.Resources
import android.os.SystemProperties
import android.os.Trace
+import android.os.Trace.TRACE_TAG_APP
import android.util.IndentingPrintWriter
import android.util.MathUtils
import android.view.CrossWindowBlurListeners
@@ -43,8 +44,8 @@ open class BlurUtils @Inject constructor(
) : Dumpable {
val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
- private val traceCookie = System.identityHashCode(this)
private var lastAppliedBlur = 0
+ private var earlyWakeupEnabled = false
init {
dumpManager.registerDumpable(javaClass.name, this)
@@ -72,6 +73,26 @@ open class BlurUtils @Inject constructor(
}
/**
+ * This method should be called before [applyBlur] so that, if needed, we can set the
+ * early-wakeup flag in SurfaceFlinger.
+ */
+ fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
+ if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
+ !supportsBlursOnWindows() || earlyWakeupEnabled
+ ) {
+ return
+ }
+ if (lastAppliedBlur == 0 && radius != 0) {
+ Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, EARLY_WAKEUP_SLICE_NAME, 0)
+ earlyWakeupEnabled = true
+ createTransaction().use {
+ it.setEarlyWakeupStart()
+ it.apply()
+ }
+ }
+ }
+
+ /**
* Applies background blurs to a {@link ViewRootImpl}.
*
* @param viewRootImpl The window root.
@@ -85,14 +106,20 @@ open class BlurUtils @Inject constructor(
createTransaction().use {
if (supportsBlursOnWindows()) {
it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
- if (lastAppliedBlur == 0 && radius != 0) {
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME,
- EARLY_WAKEUP_SLICE_NAME, traceCookie)
+ if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
+ Trace.asyncTraceForTrackBegin(
+ TRACE_TAG_APP,
+ TRACK_NAME,
+ EARLY_WAKEUP_SLICE_NAME,
+ 0
+ )
it.setEarlyWakeupStart()
+ earlyWakeupEnabled = true
}
- if (lastAppliedBlur != 0 && radius == 0) {
+ if (earlyWakeupEnabled && lastAppliedBlur != 0 && radius == 0) {
it.setEarlyWakeupEnd()
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, traceCookie)
+ Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0)
+ earlyWakeupEnabled = false
}
lastAppliedBlur = radius
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8dc78426da7d..a3bd247668fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -189,12 +189,7 @@ class NotificationShadeDepthController @Inject constructor(
scheduleUpdate()
}
- /**
- * Callback that updates the window blur value and is called only once per frame.
- */
- @VisibleForTesting
- val updateBlurCallback = Choreographer.FrameCallback {
- updateScheduled = false
+ private fun computeBlurAndZoomOut(): Pair<Int, Float> {
val animationRadius = MathUtils.constrain(shadeAnimation.radius,
blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat())
val expansionRadius = blurUtils.blurRadiusOfRatio(
@@ -232,6 +227,16 @@ class NotificationShadeDepthController @Inject constructor(
// Brightness slider removes blur, but doesn't affect zooms
blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt()
+ return Pair(blur, zoomOut)
+ }
+
+ /**
+ * Callback that updates the window blur value and is called only once per frame.
+ */
+ @VisibleForTesting
+ val updateBlurCallback = Choreographer.FrameCallback {
+ updateScheduled = false
+ val (blur, zoomOut) = computeBlurAndZoomOut()
val opaque = scrimsVisible && !blursDisabledForAppLaunch
Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
@@ -441,6 +446,8 @@ class NotificationShadeDepthController @Inject constructor(
return
}
updateScheduled = true
+ val (blur, _) = computeBlurAndZoomOut()
+ blurUtils.prepareBlur(root.viewRootImpl, blur)
choreographer.postFrameCallback(updateBlurCallback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 77550038c94a..129c8594e48e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -26,6 +26,7 @@ import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -75,9 +76,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
*/
private static final float DARK_ALPHA_BOOST = 0.67f;
/**
- * Status icons are currently drawn with the intention of being 17dp tall, but we
- * want to scale them (in a way that doesn't require an asset dump) down 2dp. So
- * 17dp * (15 / 17) = 15dp, the new height. After the first call to {@link #reloadDimens} all
+ * Status icons are currently drawn with the intention of being 17sp tall, but we
+ * want to scale them (in a way that doesn't require an asset dump) down 2sp. So
+ * 17sp * (15 / 17) = 15sp, the new height. After the first call to {@link #reloadDimens} all
* values will be in px.
*/
private float mSystemIconDesiredHeight = 15f;
@@ -144,7 +145,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
private String mNumberText;
private StatusBarNotification mNotification;
private final boolean mBlocked;
- private int mDensity;
+ private Configuration mConfiguration;
private boolean mNightMode;
private float mIconScale = 1.0f;
private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -198,9 +199,8 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
mNumberPain.setAntiAlias(true);
setNotification(sbn);
setScaleType(ScaleType.CENTER);
- mDensity = context.getResources().getDisplayMetrics().densityDpi;
- Configuration configuration = context.getResources().getConfiguration();
- mNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
+ mNightMode = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
initializeDecorColor();
reloadDimens();
@@ -214,7 +214,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
mAlwaysScaleIcon = true;
reloadDimens();
maybeUpdateIconScaleDimens();
- mDensity = context.getResources().getDisplayMetrics().densityDpi;
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
}
/** Should always be preceded by {@link #reloadDimens()} */
@@ -231,12 +231,17 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
private void updateIconScaleForNotifications() {
final float imageBounds = mIncreasedSize ?
mStatusBarIconDrawingSizeIncreased : mStatusBarIconDrawingSize;
- final int outerBounds = mStatusBarIconSize;
- mIconScale = imageBounds / (float)outerBounds;
+ float iconHeight = getIconHeight();
+ if (iconHeight != 0) {
+ mIconScale = imageBounds / iconHeight;
+ } else {
+ final int outerBounds = mStatusBarIconSize;
+ mIconScale = imageBounds / (float) outerBounds;
+ }
updatePivot();
}
- // Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height
+ // Makes sure that all icons are scaled to the same height (15sp). If we cannot get a height
// for the icon, it uses the default SCALE (15f / 17f) which is the old behavior
private void updateIconScaleForSystemIcons() {
float iconHeight = getIconHeight();
@@ -267,12 +272,10 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- int density = newConfig.densityDpi;
- if (density != mDensity) {
- mDensity = density;
- reloadDimens();
- updateDrawable();
- maybeUpdateIconScaleDimens();
+ final int configDiff = newConfig.diff(mConfiguration);
+ mConfiguration.setTo(newConfig);
+ if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) {
+ updateIconDimens();
}
boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
@@ -282,6 +285,15 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
}
+ /**
+ * Update the icon dimens and drawable with current resources
+ */
+ public void updateIconDimens() {
+ reloadDimens();
+ updateDrawable();
+ maybeUpdateIconScaleDimens();
+ }
+
private void reloadDimens() {
boolean applyRadius = mDotRadius == mStaticDotRadius;
Resources res = getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 534edb97bc5d..a058bf88e2e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -25,6 +25,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE
import android.annotation.ColorInt;
import android.content.Context;
import android.graphics.Rect;
+import android.util.Log;
import android.view.InsetsFlags;
import android.view.ViewDebug;
import android.view.WindowInsetsController.Appearance;
@@ -35,13 +36,17 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.Compile;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Date;
import javax.inject.Inject;
@@ -51,10 +56,14 @@ import javax.inject.Inject;
@SysUISingleton
public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable {
+ private static final String TAG = "LightBarController";
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+
private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
private final SysuiDarkIconDispatcher mStatusBarIconController;
private final BatteryController mBatteryController;
+ private final boolean mUseNewLightBarLogic;
private BiometricUnlockController mBiometricUnlockController;
private LightBarTransitionsController mNavigationBarController;
@@ -67,13 +76,17 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
private final int mLightIconColor;
/**
- * Whether the navigation bar should be light factoring in already how much alpha the scrim has
+ * Whether the navigation bar should be light factoring in already how much alpha the scrim has.
+ * "Light" refers to the background color of the navigation bar, so when this is true,
+ * it's referring to a state where the navigation bar icons are tinted dark.
*/
private boolean mNavigationLight;
/**
- * Whether the flags indicate that a light status bar is requested. This doesn't factor in the
- * scrim alpha yet.
+ * Whether the flags indicate that a light navigation bar is requested.
+ * "Light" refers to the background color of the navigation bar, so when this is true,
+ * it's referring to a state where the navigation bar icons would be tinted dark.
+ * This doesn't factor in the scrim alpha yet.
*/
private boolean mHasLightNavigationBar;
@@ -82,22 +95,34 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
* {@link #mNavigationLight} {@code false}.
*/
private boolean mForceDarkForScrim;
+ /**
+ * {@code true} if {@link #mHasLightNavigationBar} should be ignored and forcefully make
+ * {@link #mNavigationLight} {@code true}.
+ */
+ private boolean mForceLightForScrim;
private boolean mQsCustomizing;
+ private boolean mQsExpanded;
+ private boolean mGlobalActionsVisible;
private boolean mDirectReplying;
private boolean mNavbarColorManagedByIme;
private boolean mIsCustomizingForBackNav;
+ private String mLastSetScrimStateLog;
+ private String mLastNavigationBarAppearanceChangedLog;
+
@Inject
public LightBarController(
Context ctx,
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
+ FeatureFlags featureFlags,
DumpManager dumpManager,
DisplayTracker displayTracker) {
+ mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC);
mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone);
mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone);
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
@@ -159,9 +184,42 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
final boolean last = mNavigationLight;
mHasLightNavigationBar = isLight(appearance, navigationBarMode,
APPEARANCE_LIGHT_NAVIGATION_BARS);
- mNavigationLight = mHasLightNavigationBar
- && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
- && !mQsCustomizing;
+ if (mUseNewLightBarLogic) {
+ final boolean ignoreScrimForce = mDirectReplying && mNavbarColorManagedByIme;
+ final boolean darkForScrim = mForceDarkForScrim && !ignoreScrimForce;
+ final boolean lightForScrim = mForceLightForScrim && !ignoreScrimForce;
+ final boolean darkForQs = mQsCustomizing || mQsExpanded || mGlobalActionsVisible;
+ mNavigationLight =
+ ((mHasLightNavigationBar && !darkForScrim) || lightForScrim) && !darkForQs;
+ mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()"
+ + " appearance=" + appearance
+ + " nbModeChanged=" + nbModeChanged
+ + " navigationBarMode=" + navigationBarMode
+ + " navbarColorManagedByIme=" + navbarColorManagedByIme
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " ignoreScrimForce=" + ignoreScrimForce
+ + " darkForScrim=" + darkForScrim
+ + " lightForScrim=" + lightForScrim
+ + " darkForQs=" + darkForQs
+ + " mNavigationLight=" + mNavigationLight
+ + " last=" + last
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ } else {
+ mNavigationLight = mHasLightNavigationBar
+ && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
+ && !mQsCustomizing;
+ mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()"
+ + " appearance=" + appearance
+ + " nbModeChanged=" + nbModeChanged
+ + " navigationBarMode=" + navigationBarMode
+ + " navbarColorManagedByIme=" + navbarColorManagedByIme
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " mNavigationLight=" + mNavigationLight
+ + " last=" + last
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ }
if (mNavigationLight != last) {
updateNavigation();
}
@@ -188,6 +246,20 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
reevaluate();
}
+ /** Set if Quick Settings is fully expanded, which affects notification scrim visibility */
+ public void setQsExpanded(boolean expanded) {
+ if (mQsExpanded == expanded) return;
+ mQsExpanded = expanded;
+ reevaluate();
+ }
+
+ /** Set if Global Actions dialog is visible, which requires dark mode (light buttons) */
+ public void setGlobalActionsVisible(boolean visible) {
+ if (mGlobalActionsVisible == visible) return;
+ mGlobalActionsVisible = visible;
+ reevaluate();
+ }
+
/**
* Controls the light status bar temporarily for back navigation.
* @param appearance the custmoized appearance.
@@ -225,16 +297,52 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
public void setScrimState(ScrimState scrimState, float scrimBehindAlpha,
GradientColors scrimInFrontColor) {
- boolean forceDarkForScrimLast = mForceDarkForScrim;
- // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold.
- // This enables IMEs to control the navigation bar color.
- // For other cases, scrim should be able to veto the light navigation bar.
- mForceDarkForScrim = scrimState != ScrimState.BOUNCER
- && scrimState != ScrimState.BOUNCER_SCRIMMED
- && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD
- && !scrimInFrontColor.supportsDarkText();
- if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) {
- reevaluate();
+ if (mUseNewLightBarLogic) {
+ boolean forceDarkForScrimLast = mForceDarkForScrim;
+ boolean forceLightForScrimLast = mForceLightForScrim;
+ final boolean forceForScrim =
+ scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD;
+ final boolean scrimColorIsLight = scrimInFrontColor.supportsDarkText();
+
+ mForceDarkForScrim = forceForScrim && !scrimColorIsLight;
+ mForceLightForScrim = forceForScrim && scrimColorIsLight;
+ if (mHasLightNavigationBar) {
+ if (mForceDarkForScrim != forceDarkForScrimLast) reevaluate();
+ } else {
+ if (mForceLightForScrim != forceLightForScrimLast) reevaluate();
+ }
+ mLastSetScrimStateLog = "setScrimState()"
+ + " scrimState=" + scrimState
+ + " scrimBehindAlpha=" + scrimBehindAlpha
+ + " scrimInFrontColor=" + scrimInFrontColor
+ + " forceForScrim=" + forceForScrim
+ + " scrimColorIsLight=" + scrimColorIsLight
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " mForceDarkForScrim=" + mForceDarkForScrim
+ + " mForceLightForScrim=" + mForceLightForScrim
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastSetScrimStateLog);
+ } else {
+ boolean forceDarkForScrimLast = mForceDarkForScrim;
+ // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold.
+ // This enables IMEs to control the navigation bar color.
+ // For other cases, scrim should be able to veto the light navigation bar.
+ // NOTE: this was also wrong for S and has been removed in the new logic.
+ mForceDarkForScrim = scrimState != ScrimState.BOUNCER
+ && scrimState != ScrimState.BOUNCER_SCRIMMED
+ && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD
+ && !scrimInFrontColor.supportsDarkText();
+ if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) {
+ reevaluate();
+ }
+ mLastSetScrimStateLog = "setScrimState()"
+ + " scrimState=" + scrimState
+ + " scrimBehindAlpha=" + scrimBehindAlpha
+ + " scrimInFrontColor=" + scrimInFrontColor
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " mForceDarkForScrim=" + mForceDarkForScrim
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastSetScrimStateLog);
}
}
@@ -309,16 +417,24 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
pw.print(mAppearanceRegions[i].toString()); pw.print(" isLight="); pw.println(isLight);
}
- pw.print(" mNavigationLight="); pw.print(mNavigationLight);
+ pw.print(" mNavigationLight="); pw.println(mNavigationLight);
pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar);
-
+ pw.println();
pw.print(" mStatusBarMode="); pw.print(mStatusBarMode);
pw.print(" mNavigationBarMode="); pw.println(mNavigationBarMode);
-
- pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim);
- pw.print(" mQsCustomizing="); pw.print(mQsCustomizing);
+ pw.println();
+ pw.print(" mForceDarkForScrim="); pw.println(mForceDarkForScrim);
+ pw.print(" mForceLightForScrim="); pw.println(mForceLightForScrim);
+ pw.println();
+ pw.print(" mQsCustomizing="); pw.println(mQsCustomizing);
+ pw.print(" mQsExpanded="); pw.println(mQsExpanded);
+ pw.print(" mGlobalActionsVisible="); pw.println(mGlobalActionsVisible);
pw.print(" mDirectReplying="); pw.println(mDirectReplying);
pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme);
+ pw.println();
+ pw.println(" Recent Calculation Logs:");
+ pw.print(" "); pw.println(mLastSetScrimStateLog);
+ pw.print(" "); pw.println(mLastNavigationBarAppearanceChangedLog);
pw.println();
@@ -344,6 +460,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
private final DarkIconDispatcher mDarkIconDispatcher;
private final BatteryController mBatteryController;
private final NavigationModeController mNavModeController;
+ private final FeatureFlags mFeatureFlags;
private final DumpManager mDumpManager;
private final DisplayTracker mDisplayTracker;
@@ -352,12 +469,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
+ FeatureFlags featureFlags,
DumpManager dumpManager,
DisplayTracker displayTracker) {
mDarkIconDispatcher = darkIconDispatcher;
mBatteryController = batteryController;
mNavModeController = navModeController;
+ mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
mDisplayTracker = displayTracker;
}
@@ -365,7 +484,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
/** Create an {@link LightBarController} */
public LightBarController create(Context context) {
return new LightBarController(context, mDarkIconDispatcher, mBatteryController,
- mNavModeController, mDumpManager, mDisplayTracker);
+ mNavModeController, mFeatureFlags, mDumpManager, mDisplayTracker);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 006a029de8e0..b9a12e28b8ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -306,7 +306,7 @@ public class NotificationIconContainer extends ViewGroup {
public void applyIconStates() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- ViewState childState = mIconStates.get(child);
+ IconState childState = mIconStates.get(child);
if (childState != null) {
childState.applyToView(child);
}
@@ -339,6 +339,7 @@ public class NotificationIconContainer extends ViewGroup {
}
}
if (child instanceof StatusBarIconView) {
+ ((StatusBarIconView) child).updateIconDimens();
((StatusBarIconView) child).setDozing(mDozing, false, 0);
}
}
@@ -447,9 +448,14 @@ public class NotificationIconContainer extends ViewGroup {
@VisibleForTesting
boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd,
float iconSize) {
- // Layout end, as used here, does not include padding end.
- final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize;
- return translationX >= overflowX;
+ if (isLastChild) {
+ return translationX + iconSize > layoutEnd;
+ } else {
+ // If the child is not the last child, we need to ensure that we have room for the next
+ // icon and the dot. The dot could be as large as an icon, so verify that we have room
+ // for 2 icons.
+ return translationX + iconSize * 2f > layoutEnd;
+ }
}
/**
@@ -489,10 +495,7 @@ public class NotificationIconContainer extends ViewGroup {
// First icon to overflow.
if (firstOverflowIndex == -1 && isOverflowing) {
firstOverflowIndex = i;
- mVisualOverflowStart = layoutEnd - mIconSize;
- if (forceOverflow || mIsStaticLayout) {
- mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
- }
+ mVisualOverflowStart = translationX;
}
final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
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 51c56a021825..fdb772bc6c13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -42,6 +42,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
import com.android.internal.util.function.TriConsumer;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -249,6 +250,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
private final FeatureFlags mFeatureFlags;
+ private final boolean mUseNewLightBarLogic;
private Consumer<Integer> mScrimVisibleListener;
private boolean mBlankScreen;
private boolean mScreenBlankingCallbackCalled;
@@ -306,6 +308,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mFeatureFlags = featureFlags;
+ mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC);
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
mKeyguardStateController = keyguardStateController;
@@ -1159,7 +1162,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (mClipsQsScrim && mQsBottomVisible) {
alpha = mNotificationsAlpha;
}
- mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors());
+ if (mUseNewLightBarLogic) {
+ mScrimStateListener.accept(mState, alpha, mColors);
+ } else {
+ // NOTE: This wasn't wrong, but it implied that each scrim might have different colors,
+ // when in fact they all share the same GradientColors instance, which we own.
+ mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors());
+ }
}
private void dispatchScrimsVisible() {
@@ -1487,8 +1496,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor();
mColors.setMainColor(background);
mColors.setSecondaryColor(accent);
- mColors.setSupportsDarkText(
- ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5);
+ if (mUseNewLightBarLogic) {
+ final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
+ mColors.setSupportsDarkText(isBackgroundLight);
+ } else {
+ // NOTE: This was totally backward, but LightBarController was flipping it back.
+ // There may be other consumers of this which would struggle though
+ mColors.setSupportsDarkText(
+ ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5);
+ }
mNeedsDrawableColorUpdate = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index a8a834f1e8f4..678873c0165c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -203,8 +203,7 @@ public interface StatusBarIconController {
@Override
protected LayoutParams onCreateLayoutParams() {
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+ LinearLayout.LayoutParams lp = super.onCreateLayoutParams();
lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
return lp;
}
@@ -370,7 +369,7 @@ public interface StatusBarIconController {
private final MobileIconsViewModel mMobileIconsViewModel;
protected final Context mContext;
- protected final int mIconSize;
+ protected int mIconSize;
// Whether or not these icons show up in dumpsys
protected boolean mShouldLog = false;
private StatusBarIconController mController;
@@ -395,10 +394,10 @@ public interface StatusBarIconController {
mStatusBarPipelineFlags = statusBarPipelineFlags;
mMobileContextProvider = mobileContextProvider;
mContext = group.getContext();
- mIconSize = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_icon_size);
mLocation = location;
+ reloadDimens();
+
if (statusBarPipelineFlags.runNewMobileIconsBackend()) {
// This starts the flow for the new pipeline, and will notify us of changes if
// {@link StatusBarPipelineFlags#useNewMobileIcons} is also true.
@@ -609,13 +608,9 @@ public interface StatusBarIconController {
mGroup.removeAllViews();
}
- protected void onDensityOrFontScaleChanged() {
- for (int i = 0; i < mGroup.getChildCount(); i++) {
- View child = mGroup.getChildAt(i);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
- child.setLayoutParams(lp);
- }
+ protected void reloadDimens() {
+ mIconSize = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_icon_size);
}
private void setHeightAndCenter(ImageView imageView, int height) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 3a184239ac43..80d5651a65dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -109,6 +109,7 @@ public class StatusBarIconControllerImpl implements Tunable,
}
group.setController(this);
+ group.reloadDimens();
mIconGroups.add(group);
List<Slot> allSlots = mStatusBarIconList.getSlots();
for (int i = 0; i < allSlots.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 26c17674ab10..ddbfd43f9bf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -22,6 +22,8 @@ import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -72,13 +74,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
// Any ignored icon will never be added as a child
private ArrayList<String> mIgnoredSlots = new ArrayList<>();
+ private Configuration mConfiguration;
+
public StatusIconContainer(Context context) {
this(context, null);
}
public StatusIconContainer(Context context, AttributeSet attrs) {
super(context, attrs);
- initDimens();
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
+ reloadDimens();
setWillNotDraw(!DEBUG_OVERFLOW);
}
@@ -95,7 +100,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
return mShouldRestrictIcons;
}
- private void initDimens() {
+ private void reloadDimens() {
// This is the same value that StatusBarIconView uses
mIconDotFrameWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
@@ -211,6 +216,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
child.setTag(R.id.status_bar_view_state_tag, null);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ final int configDiff = newConfig.diff(mConfiguration);
+ mConfiguration.setTo(newConfig);
+ if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) {
+ reloadDimens();
+ }
+ }
+
/**
* Add a name of an icon slot to be ignored. It will not show up nor be measured
* @param slotName name of the icon as it exists in
@@ -348,13 +363,17 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
int totalVisible = mLayoutStates.size();
int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1;
- mUnderflowStart = 0;
+ // Init mUnderflowStart value with the offset to let the dot be placed next to battery icon.
+ // It to prevent if the underflow happens at rightest(totalVisible - 1) child then break the
+ // for loop with mUnderflowStart staying 0(initial value), causing the dot be placed at the
+ // leftest side.
+ mUnderflowStart = (int) Math.max(contentStart, width - getPaddingEnd() - mUnderflowWidth);
int visible = 0;
int firstUnderflowIndex = -1;
for (int i = totalVisible - 1; i >= 0; i--) {
StatusIconState state = mLayoutStates.get(i);
// Allow room for underflow if we found we need it in onMeasure
- if (mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth))
+ if ((mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth)))
|| (mShouldRestrictIcons && (visible >= maxVisible))) {
firstUnderflowIndex = i;
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index 98cde2a049da..abedd3220c93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -28,6 +28,11 @@ public interface CastController extends CallbackController<Callback>, Dumpable {
void startCasting(CastDevice device);
void stopCasting(CastDevice device);
+ /**
+ * @return whether we have a connected device.
+ */
+ boolean hasConnectedCastDevice();
+
public interface Callback {
void onCastDevicesChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 7290a1a82070..f7b601b9d284 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -217,6 +217,12 @@ public class CastControllerImpl implements CastController {
}
}
+ @Override
+ public boolean hasConnectedCastDevice() {
+ return getCastDevices().stream().anyMatch(
+ castDevice -> castDevice.state == CastDevice.STATE_CONNECTED);
+ }
+
private void setProjection(MediaProjectionInfo projection, boolean started) {
boolean changed = false;
final MediaProjectionInfo oldProjection = mProjection;
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 412b3150489c..27aaa6828036 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -22,7 +22,6 @@ import android.content.Context
import android.hardware.BatteryState
import android.hardware.input.InputManager
import android.hardware.input.InputSettings
-import android.os.Build
import android.os.Handler
import android.util.ArrayMap
import android.util.Log
@@ -35,6 +34,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.shared.hardware.hasInputDevice
import com.android.systemui.shared.hardware.isInternalStylusSource
import java.util.concurrent.CopyOnWriteArrayList
@@ -81,7 +81,7 @@ constructor(
fun startListener() {
handler.post {
if (hasStarted) return@post
- logDebug { "Listener has started." }
+ debugLog { "Listener has started." }
hasStarted = true
isInUsiSession =
@@ -109,7 +109,7 @@ constructor(
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
- logDebug {
+ debugLog {
"Stylus InputDevice added: $deviceId ${device.name}, " +
"External: ${device.isExternal}"
}
@@ -134,7 +134,7 @@ constructor(
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
- logDebug { "Stylus InputDevice changed: $deviceId ${device.name}" }
+ debugLog { "Stylus InputDevice changed: $deviceId ${device.name}" }
val currAddress: String? = device.bluetoothAddress
val prevAddress: String? = inputDeviceAddressMap[deviceId]
@@ -155,7 +155,7 @@ constructor(
if (!hasStarted) return
if (!inputDeviceAddressMap.contains(deviceId)) return
- logDebug { "Stylus InputDevice removed: $deviceId" }
+ debugLog { "Stylus InputDevice removed: $deviceId" }
unregisterBatteryListener(deviceId)
@@ -180,7 +180,7 @@ constructor(
val isCharging = String(value) == "true"
- logDebug {
+ debugLog {
"Charging state metadata changed for device $inputDeviceId " +
"${device.address}: $isCharging"
}
@@ -199,7 +199,7 @@ constructor(
handler.post {
if (!hasStarted) return@post
- logDebug {
+ debugLog {
"Battery state changed for $deviceId. " +
"batteryState present: ${batteryState.isPresent}, " +
"capacity: ${batteryState.capacity}"
@@ -247,7 +247,7 @@ constructor(
if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
if (InputSettings.isStylusEverUsed(context)) return
- logDebug { "Stylus used for the first time." }
+ debugLog { "Stylus used for the first time." }
InputSettings.setStylusEverUsed(context, true)
executeStylusCallbacks { cb -> cb.onStylusFirstUsed() }
}
@@ -264,7 +264,7 @@ constructor(
val hasBtConnection = if (inputDeviceBtSessionIdMap.isEmpty()) 0 else 1
if (batteryStateValid && usiSessionId == null) {
- logDebug { "USI battery newly present, entering new USI session: $deviceId" }
+ debugLog { "USI battery newly present, entering new USI session: $deviceId" }
usiSessionId = instanceIdSequence.newInstanceId()
uiEventLogger.logWithInstanceIdAndPosition(
StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
@@ -274,7 +274,7 @@ constructor(
hasBtConnection,
)
} else if (!batteryStateValid && usiSessionId != null) {
- logDebug { "USI battery newly absent, exiting USI session: $deviceId" }
+ debugLog { "USI battery newly absent, exiting USI session: $deviceId" }
uiEventLogger.logWithInstanceIdAndPosition(
StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED,
0,
@@ -291,7 +291,7 @@ constructor(
btAddress: String,
btConnected: Boolean
) {
- logDebug {
+ debugLog {
"Bluetooth stylus ${if (btConnected) "connected" else "disconnected"}:" +
" $deviceId $btAddress"
}
@@ -386,9 +386,3 @@ constructor(
val TAG = StylusManager::class.simpleName.orEmpty()
}
}
-
-private inline fun logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) {
- Log.d(StylusManager.TAG, message())
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index 21b0efadb8d5..6eddd9eb7ad2 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -26,7 +26,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.hardware.BatteryState
import android.hardware.input.InputManager
-import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.UserHandle
@@ -40,6 +39,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.shared.hardware.hasInputDevice
import com.android.systemui.shared.hardware.isAnyStylusSource
import com.android.systemui.util.NotificationChannels
@@ -110,7 +110,7 @@ constructor(
inputDeviceId = deviceId
batteryCapacity = batteryState.capacity
- logDebug {
+ debugLog {
"Updating notification battery state to $batteryCapacity " +
"for InputDevice $deviceId."
}
@@ -130,14 +130,14 @@ constructor(
handler.post updateSuppressed@{
if (suppressed == suppress) return@updateSuppressed
- logDebug { "Updating notification suppression to $suppress." }
+ debugLog { "Updating notification suppression to $suppress." }
suppressed = suppress
refresh()
}
}
private fun hideNotification() {
- logDebug { "Cancelling USI low battery notification." }
+ debugLog { "Cancelling USI low battery notification." }
instanceId = null
notificationManager.cancel(USI_NOTIFICATION_ID)
}
@@ -160,7 +160,7 @@ constructor(
.setAutoCancel(true)
.build()
- logDebug { "Show or update USI low battery notification at $batteryCapacity." }
+ debugLog { "Show or update USI low battery notification at $batteryCapacity." }
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN)
notificationManager.notify(USI_NOTIFICATION_ID, notification)
}
@@ -188,12 +188,12 @@ constructor(
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
ACTION_DISMISSED_LOW_BATTERY -> {
- logDebug { "USI low battery notification dismissed." }
+ debugLog { "USI low battery notification dismissed." }
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED)
updateSuppression(true)
}
ACTION_CLICKED_LOW_BATTERY -> {
- logDebug { "USI low battery notification clicked." }
+ debugLog { "USI low battery notification clicked." }
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED)
updateSuppression(true)
if (inputDeviceId == null) return
@@ -263,9 +263,3 @@ constructor(
@VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args"
}
}
-
-private inline fun logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) {
- Log.d(StylusUsiPowerUI.TAG, message())
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index b3e7cb0c77eb..e60f9b65dc1c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -33,6 +33,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.view.Display;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
@@ -347,12 +348,16 @@ public final class WMShell implements
void initDesktopMode(DesktopMode desktopMode) {
desktopMode.addVisibleTasksListener(
new DesktopModeTaskRepository.VisibleTasksListener() {
- @Override
- public void onVisibilityChanged(boolean hasFreeformTasks) {
- mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, hasFreeformTasks)
- .commitUpdate(mDisplayTracker.getDefaultDisplayId());
- }
- }, mSysUiMainExecutor);
+ @Override
+ public void onVisibilityChanged(int displayId, boolean hasFreeformTasks) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
+ hasFreeformTasks)
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+ }
+ // TODO(b/278084491): update sysui state for changes on other displays
+ }
+ }, mSysUiMainExecutor);
}
@Override
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e2b568cfea77..080be6d8cf25 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -196,6 +196,17 @@
android:exported="false"
android:permission="com.android.systemui.permission.SELF"
android:excludeFromRecents="true" />
+
+ <activity
+ android:name="com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity"
+ android:exported="false"
+ android:permission="com.android.systemui.permission.SELF"
+ android:excludeFromRecents="true" >
+ <intent-filter>
+ <action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="android.testing.TestableInstrumentation"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index f4df26dec89e..8a05a37ad0dd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -682,8 +682,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
}
@Test
- public void testReinflateViewFlipper_asyncBouncerFlagOn() {
- when(mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)).thenReturn(true);
+ public void testReinflateViewFlipper() {
KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedCallback =
controller -> {
};
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index a20875b5d9de..6e37ee791bd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -40,14 +40,19 @@ import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import org.junit.After
import org.junit.Ignore
import org.junit.Rule
@@ -87,13 +92,26 @@ class AuthContainerViewTest : SysuiTestCase() {
@Mock
lateinit var interactionJankMonitor: InteractionJankMonitor
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val biometricPromptRepository = FakePromptRepository()
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
private val credentialInteractor = FakeCredentialInteractor()
private val bpCredentialInteractor = BiometricPromptCredentialInteractor(
Dispatchers.Main.immediate,
biometricPromptRepository,
credentialInteractor
)
+ private val displayStateInteractor = DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
+
+ private val authBiometricFingerprintViewModel = AuthBiometricFingerprintViewModel(
+ displayStateInteractor
+ )
private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
private var authContainer: TestAuthContainerView? = null
@@ -469,9 +487,10 @@ class AuthContainerViewTest : SysuiTestCase() {
lockPatternUtils,
interactionJankMonitor,
{ bpCredentialInteractor },
+ { authBiometricFingerprintViewModel },
{ credentialViewModel },
Handler(TestableLooper.get(this).looper),
- FakeExecutor(FakeSystemClock())
+ fakeExecutor
) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 4f24b3ab5e09..a326cc7045dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -93,6 +93,7 @@ import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
@@ -172,6 +173,8 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private BiometricPromptCredentialInteractor mBiometricPromptCredentialInteractor;
@Mock
+ private AuthBiometricFingerprintViewModel mAuthBiometricFingerprintViewModel;
+ @Mock
private CredentialViewModel mCredentialViewModel;
@Mock
private UdfpsUtils mUdfpsUtils;
@@ -995,8 +998,9 @@ public class AuthControllerTest extends SysuiTestCase {
() -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle,
mPanelInteractionDetector, mUserManager, mLockPatternUtils, mUdfpsLogger,
mLogContextInteractor, () -> mBiometricPromptCredentialInteractor,
- () -> mCredentialViewModel, mInteractionJankMonitor, mHandler,
- mBackgroundExecutor, mVibratorHelper, mUdfpsUtils);
+ () -> mAuthBiometricFingerprintViewModel, () -> mCredentialViewModel,
+ mInteractionJankMonitor, mHandler, mBackgroundExecutor, mVibratorHelper,
+ mUdfpsUtils);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index e6334cf2e4a1..40d9009ee81d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -55,6 +55,9 @@ import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -66,7 +69,9 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -90,6 +95,8 @@ import org.mockito.junit.MockitoJUnit
private const val DISPLAY_ID = 2
private const val SENSOR_ID = 1
+private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
+
@SmallTest
@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@@ -112,7 +119,12 @@ class SideFpsControllerTest : SysuiTestCase() {
private lateinit var keyguardBouncerRepository: FakeKeyguardBouncerRepository
private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ private lateinit var displayStateInteractor: DisplayStateInteractor
+
private val executor = FakeExecutor(FakeSystemClock())
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+ private val testScope = TestScope(StandardTestDispatcher())
+
private lateinit var overlayController: ISidefpsController
private lateinit var sideFpsController: SideFpsController
@@ -142,6 +154,13 @@ class SideFpsControllerTest : SysuiTestCase() {
FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(),
)
+ displayStateInteractor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ context,
+ executor,
+ rearDisplayStateRepository
+ )
context.addMockSystemService(DisplayManager::class.java, displayManager)
context.addMockSystemService(WindowManager::class.java, windowManager)
@@ -168,6 +187,7 @@ class SideFpsControllerTest : SysuiTestCase() {
isReverseDefaultRotation: Boolean = false,
initInfo: DisplayInfo.() -> Unit = {},
windowInsets: WindowInsets = insetsForSmallNavbar(),
+ inRearDisplayMode: Boolean = false,
block: () -> Unit
) {
this.deviceConfig = deviceConfig
@@ -228,6 +248,13 @@ class SideFpsControllerTest : SysuiTestCase() {
isReverseDefaultRotation
)
+ val rearDisplayDeviceStates =
+ if (inRearDisplayMode) intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE) else intArrayOf()
+ sideFpsControllerContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.array.config_rearDisplayDeviceStates,
+ rearDisplayDeviceStates
+ )
+
sideFpsController =
SideFpsController(
sideFpsControllerContext,
@@ -237,12 +264,14 @@ class SideFpsControllerTest : SysuiTestCase() {
activityTaskManager,
overviewProxyService,
displayManager,
+ displayStateInteractor,
executor,
handler,
alternateBouncerInteractor,
TestCoroutineScope(),
- dumpManager,
+ dumpManager
)
+ rearDisplayStateRepository.setIsInRearDisplayMode(inRearDisplayMode)
overlayController =
ArgumentCaptor.forClass(ISidefpsController::class.java)
@@ -584,10 +613,62 @@ class SideFpsControllerTest : SysuiTestCase() {
verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
}
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_0() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_0 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_90() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_90 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_180 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_270() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_270 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) {
sideFpsController.overlayOffsets = sensorLocation
}
+ private fun verifySfpsIndicator_notAdded_InRearDisplayMode() {
+ sideFpsController.overlayOffsets = sensorLocation
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager, never()).addView(any(), any())
+ }
+
fun alternateBouncerVisibility_showAndHideSideFpsUI() = testWithDisplay {
// WHEN alternate bouncer is visible
keyguardBouncerRepository.setAlternateVisible(true)
@@ -624,7 +705,7 @@ class SideFpsControllerTest : SysuiTestCase() {
* in other rotations have been omitted.
*/
@Test
- fun verifiesIndicatorPlacementForXAlignedSensor_0() {
+ fun verifiesIndicatorPlacementForXAlignedSensor_0() =
testWithDisplay(
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = false,
@@ -641,7 +722,6 @@ class SideFpsControllerTest : SysuiTestCase() {
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
}
- }
/**
* {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270
@@ -650,7 +730,7 @@ class SideFpsControllerTest : SysuiTestCase() {
* correctly, tests for indicator placement in other rotations have been omitted.
*/
@Test
- fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() {
+ fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() =
testWithDisplay(
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = true,
@@ -667,7 +747,6 @@ class SideFpsControllerTest : SysuiTestCase() {
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
}
- }
/**
* {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt
new file mode 100644
index 000000000000..dfe8d36504d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.data.repository
+
+import android.hardware.devicestate.DeviceStateManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2
+private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class RearDisplayStateRepositoryTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var deviceStateManager: DeviceStateManager
+ private lateinit var underTest: RearDisplayStateRepository
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+ @Captor
+ private lateinit var callbackCaptor: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
+
+ @Before
+ fun setUp() {
+ val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE)
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.array.config_rearDisplayDeviceStates,
+ rearDisplayDeviceStates
+ )
+
+ underTest =
+ RearDisplayStateRepositoryImpl(
+ testScope.backgroundScope,
+ mContext,
+ deviceStateManager,
+ fakeExecutor
+ )
+ }
+
+ @Test
+ fun updatesIsInRearDisplayMode_whenRearDisplayStateChanges() =
+ testScope.runTest {
+ val isInRearDisplayMode = collectLastValue(underTest.isInRearDisplayMode)
+ runCurrent()
+
+ val callback = deviceStateManager.captureCallback()
+
+ callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE)
+ assertThat(isInRearDisplayMode()).isFalse()
+
+ callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE)
+ assertThat(isInRearDisplayMode()).isTrue()
+ }
+}
+
+private fun DeviceStateManager.captureCallback() =
+ withArgCaptor<DeviceStateManager.DeviceStateCallback> {
+ verify(this@captureCallback).registerCallback(any(), capture())
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
new file mode 100644
index 000000000000..2217c5c677d5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
@@ -0,0 +1,84 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DisplayStateInteractorImplTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+
+ @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
+ private lateinit var interactor: DisplayStateInteractorImpl
+
+ @Before
+ fun setup() {
+ interactor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
+ interactor.setScreenSizeFoldProvider(screenSizeFoldProvider)
+ }
+
+ @Test
+ fun isInRearDisplayModeChanges() =
+ testScope.runTest {
+ val isInRearDisplayMode = collectLastValue(interactor.isInRearDisplayMode)
+
+ rearDisplayStateRepository.setIsInRearDisplayMode(false)
+ assertThat(isInRearDisplayMode()).isFalse()
+
+ rearDisplayStateRepository.setIsInRearDisplayMode(true)
+ assertThat(isInRearDisplayMode()).isTrue()
+ }
+
+ @Test
+ fun isFoldedChanges() =
+ testScope.runTest {
+ val isFolded = collectLastValue(interactor.isFolded)
+ runCurrent()
+ val callback = screenSizeFoldProvider.captureCallback()
+
+ callback.onFoldUpdated(isFolded = true)
+ assertThat(isFolded()).isTrue()
+
+ callback.onFoldUpdated(isFolded = false)
+ assertThat(isFolded()).isFalse()
+ }
+}
+
+private fun FoldProvider.captureCallback() =
+ withArgCaptor<FoldProvider.FoldCallback> {
+ verify(this@captureCallback).registerCallback(capture(), any())
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt
new file mode 100644
index 000000000000..0c210e51cb90
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt
@@ -0,0 +1,69 @@
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.content.res.Configuration
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class AuthBiometricFingerprintViewModelTest : SysuiTestCase() {
+
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+ private lateinit var interactor: DisplayStateInteractor
+ private lateinit var viewModel: AuthBiometricFingerprintViewModel
+
+ @Before
+ fun setup() {
+ interactor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
+ viewModel = AuthBiometricFingerprintViewModel(interactor)
+ }
+
+ @Test
+ fun iconUpdates_onConfigurationChanged() {
+ testScope.runTest {
+ runCurrent()
+ val testConfig = Configuration()
+ val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
+ val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
+ val currentIcon = collectLastValue(viewModel.iconAsset)
+
+ testConfig.smallestScreenWidthDp = folded
+ viewModel.onConfigurationChanged(testConfig)
+ val foldedIcon = currentIcon()
+
+ testConfig.smallestScreenWidthDp = unfolded
+ viewModel.onConfigurationChanged(testConfig)
+ val unfoldedIcon = currentIcon()
+
+ assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
+ }
+ }
+}
+
+internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 8795ac013bd3..c9ee1e8ef5b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -70,6 +70,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -115,6 +116,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private MetricsLogger mMetricsLogger;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private IStatusBarService mStatusBarService;
+ @Mock private LightBarController mLightBarController;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor;
@@ -166,6 +168,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mMetricsLogger,
mColorExtractor,
mStatusBarService,
+ mLightBarController,
mNotificationShadeWindowController,
mWindowManager,
mBackgroundExecutor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index b9cfc6550966..e981d627b582 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -25,13 +25,11 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -59,13 +57,14 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WorkLockActivityControllerTest extends SysuiTestCase {
- private static final int USER_ID = 333;
+ private static final int TASK_USER_ID = 333;
+ private static final int PROFILE_USER_ID = 555;
private static final int TASK_ID = 444;
private static final ActivityManager.RunningTaskInfo TASK_INFO =
new ActivityManager.RunningTaskInfo();
static {
- TASK_INFO.userId = USER_ID;
+ TASK_INFO.userId = TASK_USER_ID;
TASK_INFO.taskId = TASK_ID;
}
@@ -101,10 +100,10 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_SUCCESS);
// And the controller receives a message saying the profile is locked,
- mTaskStackListener.onTaskProfileLocked(TASK_INFO);
+ mTaskStackListener.onTaskProfileLocked(TASK_INFO, PROFILE_USER_ID);
// The overlay should start and the task the activity started in should not be removed.
- verifyStartActivity(TASK_ID, true /*taskOverlay*/);
+ verifyStartActivity(TASK_ID, true /*taskOverlay*/, PROFILE_USER_ID);
verify(mIActivityTaskManager, never()).removeTask(anyInt() /*taskId*/);
}
@@ -114,11 +113,11 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_CLASS_NOT_FOUND);
// And the controller receives a message saying the profile is locked,
- mTaskStackListener.onTaskProfileLocked(TASK_INFO);
+ mTaskStackListener.onTaskProfileLocked(TASK_INFO, PROFILE_USER_ID);
// The task the activity started in should be removed to prevent the locked task from
// being shown.
- verifyStartActivity(TASK_ID, true /*taskOverlay*/);
+ verifyStartActivity(TASK_ID, true /*taskOverlay*/, PROFILE_USER_ID);
verify(mIActivityTaskManager).removeTask(TASK_ID);
}
@@ -141,12 +140,13 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
eq(ActivityManager.getCurrentUser()));
}
- private void verifyStartActivity(int taskId, boolean taskOverlay) throws Exception {
+ private void verifyStartActivity(int taskId, boolean taskOverlay, int profileUserId)
+ throws Exception {
verify(mIActivityTaskManager).startActivityAsUser(
eq((IApplicationThread) null),
eq((String) null),
eq((String) null),
- any(Intent.class),
+ argThat(hasUserId(profileUserId)),
eq((String) null),
eq((IBinder) null),
eq((String) null),
@@ -157,24 +157,15 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
eq(ActivityManager.getCurrentUser()));
}
- private static ArgumentMatcher<Intent> hasComponent(final Context context,
- final Class<? extends Activity> activityClass) {
- return new ArgumentMatcher<Intent>() {
- @Override
- public boolean matches(Intent intent) {
- return new ComponentName(context, activityClass).equals(intent.getComponent());
- }
- };
+ private static ArgumentMatcher<Intent> hasUserId(int userId) {
+ return intent -> intent.getIntExtra(Intent.EXTRA_USER_ID, -1) == userId;
}
private static ArgumentMatcher<Bundle> hasOptions(final int taskId, final boolean overlay) {
- return new ArgumentMatcher<Bundle>() {
- @Override
- public boolean matches(Bundle item) {
- final ActivityOptions options = ActivityOptions.fromBundle(item);
- return (options.getLaunchTaskId() == taskId)
- && (options.getTaskOverlay() == overlay);
- }
+ return item -> {
+ final ActivityOptions options = ActivityOptions.fromBundle(item);
+ return (options.getLaunchTaskId() == taskId)
+ && (options.getTaskOverlay() == overlay);
};
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 0a1db60e075c..d428db7b9dda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -40,7 +40,6 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.media.utils.MediaConstants
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.R
@@ -131,7 +130,6 @@ class MediaDataManagerTest : SysuiTestCase() {
@Mock lateinit var activityStarter: ActivityStarter
@Mock lateinit var smartspaceManager: SmartspaceManager
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock lateinit var statusBarService: IStatusBarService
lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction
@@ -194,8 +192,7 @@ class MediaDataManagerTest : SysuiTestCase() {
mediaFlags = mediaFlags,
logger = logger,
smartspaceManager = smartspaceManager,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- statusBarService = statusBarService,
+ keyguardUpdateMonitor = keyguardUpdateMonitor
)
verify(tunerService)
.addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -520,136 +517,19 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
- fun testOnNotificationAdded_emptyTitle_notLoaded() {
- // GIVEN that the manager has a notification with an empty title.
+ fun testOnNotificationRemoved_emptyTitle_notConverted() {
+ // GIVEN that the manager has a notification with a resume action and empty title.
whenever(controller.metadata)
.thenReturn(
metadataBuilder
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
.build()
)
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationAdded_blankTitle_notLoaded() {
- // GIVEN that the manager has a notification with a blank title.
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationUpdated_invalidTitle_logMediaRemoved() {
- addNotificationAndLoad()
- val data = mediaDataCaptor.value
-
- verify(listener)
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
-
- reset(listener)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
- }
-
- @Test
- fun testOnNotificationRemoved_emptyTitle_notConverted() {
- // GIVEN that the manager has a notification with a resume action and empty title.
addNotificationAndLoad()
val data = mediaDataCaptor.value
val instanceId = data.instanceId
assertThat(data.resumption).isFalse()
- mediaDataManager.onMediaDataLoaded(
- KEY,
- null,
- data.copy(song = SESSION_EMPTY_TITLE, resumeAction = Runnable {})
- )
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
reset(listener)
@@ -674,15 +554,17 @@ class MediaDataManagerTest : SysuiTestCase() {
@Test
fun testOnNotificationRemoved_blankTitle_notConverted() {
// GIVEN that the manager has a notification with a resume action and blank title.
+ whenever(controller.metadata)
+ .thenReturn(
+ metadataBuilder
+ .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
+ .build()
+ )
addNotificationAndLoad()
val data = mediaDataCaptor.value
val instanceId = data.instanceId
assertThat(data.resumption).isFalse()
- mediaDataManager.onMediaDataLoaded(
- KEY,
- null,
- data.copy(song = SESSION_BLANK_TITLE, resumeAction = Runnable {})
- )
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
reset(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt
new file mode 100644
index 000000000000..36b913fc3e7b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.notetask
+
+import android.content.Context
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class LaunchNotesRoleSettingsTrampolineActivityTest : SysuiTestCase() {
+
+ @Mock lateinit var noteTaskController: NoteTaskController
+
+ @Rule
+ @JvmField
+ val activityRule =
+ ActivityTestRule<LaunchNotesRoleSettingsTrampolineActivity>(
+ /* activityFactory= */ object :
+ SingleActivityFactory<LaunchNotesRoleSettingsTrampolineActivity>(
+ LaunchNotesRoleSettingsTrampolineActivity::class.java
+ ) {
+ override fun create(intent: Intent?) =
+ LaunchNotesRoleSettingsTrampolineActivity(noteTaskController)
+ },
+ /* initialTouchMode= */ false,
+ /* launchActivity= */ false,
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @After
+ fun tearDown() {
+ activityRule.finishActivity()
+ }
+
+ @Test
+ fun startActivity_noAction_shouldLaunchNotesRoleSettingTaskWithNullEntryPoint() {
+ activityRule.launchActivity(/* startIntent= */ null)
+
+ verify(noteTaskController).startNotesRoleSetting(any(Context::class.java), eq(null))
+ }
+
+ @Test
+ fun startActivity_quickAffordanceAction_shouldLaunchNotesRoleSettingTaskWithQuickAffordanceEntryPoint() { // ktlint-disable max-line-length
+ activityRule.launchActivity(Intent(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE))
+
+ verify(noteTaskController)
+ .startNotesRoleSetting(any(Context::class.java), eq(QUICK_AFFORDANCE))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 5dbcd33ab0e6..5f897050099a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -47,6 +47,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
import com.android.systemui.notetask.NoteTaskController.Companion.SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT
import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
+import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
+import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity
import com.android.systemui.settings.FakeUserTracker
@@ -493,7 +496,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -509,7 +512,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -525,7 +528,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
}
@@ -541,7 +544,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
}
@@ -553,7 +556,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
userTracker.set(listOf(mainUserInfo), mainAndWorkProfileUsers.indexOf(mainUserInfo))
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(mainUserInfo.userHandle)
}
@@ -563,7 +566,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(workUserInfo.userHandle)
}
@@ -734,6 +737,129 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
}
// endregion
+ // region getUserForHandlingNotesTaking
+ @Test
+ fun getUserForHandlingNotesTaking_cope_quickAffordance_shouldReturnWorkProfileUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+
+ assertThat(user).isEqualTo(UserHandle.of(workUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_cope_tailButton_shouldReturnWorkProfileUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(TAIL_BUTTON)
+
+ assertThat(user).isEqualTo(UserHandle.of(workUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_cope_appClip_shouldReturnCurrentUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(APP_CLIPS)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_noManagement_quickAffordance_shouldReturnCurrentUser() {
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_noManagement_tailButton_shouldReturnCurrentUser() {
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(TAIL_BUTTON)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_noManagement_appClip_shouldReturnCurrentUser() {
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(APP_CLIPS)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+ // endregion
+
+ // startregion startNotesRoleSetting
+ @Test
+ fun startNotesRoleSetting_cope_quickAffordance_shouldStartNoteRoleIntentWithWorkProfileUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, QUICK_AFFORDANCE)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(workUserInfo.id))
+ }
+
+ @Test
+ fun startNotesRoleSetting_cope_nullEntryPoint_shouldStartNoteRoleIntentWithCurrentUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, entryPoint = null)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun startNotesRoleSetting_noManagement_quickAffordance_shouldStartNoteRoleIntentWithCurrentUser() { // ktlint-disable max-line-length
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, QUICK_AFFORDANCE)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun startNotesRoleSetting_noManagement_nullEntryPoint_shouldStartNoteRoleIntentWithCurrentUser() { // ktlint-disable max-line-length
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, entryPoint = null)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+ // endregion
+
private companion object {
const val NOTE_TASK_SHORT_LABEL = "Notetaking"
const val NOTE_TASK_ACTIVITY_NAME = "NoteTaskActivity"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index 42ef2b5ad3ab..c17d6d192698 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -18,7 +18,12 @@
package com.android.systemui.notetask.quickaffordance
+import android.app.role.RoleManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
import android.hardware.input.InputSettings
+import android.os.UserHandle
import android.os.UserManager
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
@@ -31,11 +36,18 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.notetask.NoteTaskInfoResolver
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR
import com.android.systemui.stylus.StylusManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -45,6 +57,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.verify
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
@@ -58,6 +71,8 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
@Mock lateinit var stylusManager: StylusManager
@Mock lateinit var repository: KeyguardQuickAffordanceRepository
@Mock lateinit var userManager: UserManager
+ @Mock lateinit var roleManager: RoleManager
+ @Mock lateinit var packageManager: PackageManager
private lateinit var mockitoSession: MockitoSession
@@ -69,6 +84,23 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
.mockStatic(InputSettings::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
+
+ whenever(
+ packageManager.getApplicationInfoAsUser(
+ anyString(),
+ any(ApplicationInfoFlags::class.java),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(ApplicationInfo())
+ whenever(controller.getUserForHandlingNotesTaking(any())).thenReturn(UserHandle.SYSTEM)
+ whenever(
+ roleManager.getRoleHoldersAsUser(
+ eq(RoleManager.ROLE_NOTES),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(listOf("com.google.test.notes"))
}
@After
@@ -85,6 +117,9 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
keyguardMonitor = mock(),
lazyRepository = { repository },
isEnabled = isEnabled,
+ backgroundExecutor = FakeExecutor(FakeSystemClock()),
+ roleManager = roleManager,
+ noteTaskInfoResolver = NoteTaskInfoResolver(roleManager, packageManager)
)
private fun createLockScreenStateVisible(): LockScreenState =
@@ -112,6 +147,27 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
}
@Test
+ fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() =
+ runTest {
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(true)
+ .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())
+ whenever(
+ roleManager.getRoleHoldersAsUser(
+ eq(RoleManager.ROLE_NOTES),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(emptyList())
+
+ val underTest = createUnderTest()
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Test
fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest {
TestConfig()
.setStylusEverUsed(false)
@@ -217,6 +273,39 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
}
+ // region getPickerScreenState
+ @Test
+ fun getPickerScreenState_defaultNoteAppSet_shouldReturnDefault() = runTest {
+ val underTest = createUnderTest(isEnabled = true)
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
+ }
+
+ @Test
+ fun getPickerScreenState_nodefaultNoteAppSet_shouldReturnDisable() = runTest {
+ val underTest = createUnderTest(isEnabled = true)
+ whenever(
+ roleManager.getRoleHoldersAsUser(
+ eq(RoleManager.ROLE_NOTES),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(emptyList())
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(
+ KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
+ listOf("Select a default notes app to use notetaking shortcut"),
+ actionText = "Open settings",
+ actionComponentName =
+ "${context.packageName}$COMPONENT_NAME_SEPARATOR" +
+ "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE"
+ )
+ )
+ }
+ // endregion
+
private inner class TestConfig {
fun setStylusEverUsed(value: Boolean) = also {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 88d7e9cb4de4..2dc78a323f55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -587,14 +587,14 @@ public class QSSecurityFooterTest extends SysuiTestCase {
assertEquals(addLink(mContext.getString(R.string.monitoring_description_two_named_vpns,
VPN_PACKAGE, VPN_PACKAGE_2)),
mFooterUtils.getVpnMessage(false, true, VPN_PACKAGE, VPN_PACKAGE_2));
- assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn,
- VPN_PACKAGE)),
+ assertEquals(addLink(mContext.getString(
+ R.string.monitoring_description_managed_device_named_vpn, VPN_PACKAGE)),
mFooterUtils.getVpnMessage(true, false, VPN_PACKAGE, null));
assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn,
VPN_PACKAGE)),
mFooterUtils.getVpnMessage(false, false, VPN_PACKAGE, null));
- assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn,
- VPN_PACKAGE_2)),
+ assertEquals(addLink(mContext.getString(
+ R.string.monitoring_description_managed_device_named_vpn, VPN_PACKAGE_2)),
mFooterUtils.getVpnMessage(true, true, null, VPN_PACKAGE_2));
assertEquals(addLink(mContext.getString(
R.string.monitoring_description_managed_profile_named_vpn,
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 1edc63c4417e..9a8ec88e992a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -151,6 +151,7 @@ import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -158,6 +159,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -239,6 +241,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController;
+ @Mock protected LightBarController mLightBarController;
@Mock protected NotificationStackScrollLayoutController
mNotificationStackScrollLayoutController;
@Mock protected NotificationShadeDepthController mNotificationShadeDepthController;
@@ -306,6 +309,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected ActivityStarter mActivityStarter;
@Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
@Mock protected ShadeRepository mShadeRepository;
+ @Mock private CastController mCastController;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
@@ -653,6 +657,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mNotificationRemoteInputManager,
mShadeExpansionStateManager,
mStatusBarKeyguardViewManager,
+ mLightBarController,
mNotificationStackScrollLayoutController,
mLockscreenShadeTransitionController,
mNotificationShadeDepthController,
@@ -675,7 +680,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mInteractionJankMonitor,
mShadeLog,
mKeyguardFaceAuthInteractor,
- mShadeRepository
+ mShadeRepository,
+ mCastController
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 8a9161e6d46d..1cf38732fdb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -83,10 +83,12 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import dagger.Lazy;
@@ -132,6 +134,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
@Mock private PulseExpansionHandler mPulseExpansionHandler;
@Mock private NotificationRemoteInputManager mNotificationRemoteInputManager;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private LightBarController mLightBarController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
@@ -155,6 +158,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
@Mock private ShadeLogger mShadeLogger;
@Mock private DumpManager mDumpManager;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private CastController mCastController;
private SysuiStatusBarStateController mStatusBarStateController;
@@ -221,6 +225,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
mNotificationRemoteInputManager,
mShadeExpansionStateManager,
mStatusBarKeyguardViewManager,
+ mLightBarController,
mNotificationStackScrollLayoutController,
mLockscreenShadeTransitionController,
mNotificationShadeDepthController,
@@ -243,7 +248,8 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
mInteractionJankMonitor,
mShadeLogger,
mock(KeyguardFaceAuthInteractor.class),
- mock(ShadeRepository.class)
+ mock(ShadeRepository.class),
+ mCastController
);
mFragmentListener = mQsController.getQsFragmentListener();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index beaf3009125b..c49f179c925e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -356,6 +356,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Test
fun ignoreShadeBlurUntilHidden_schedulesFrame() {
notificationShadeDepthController.blursDisabledForAppLaunch = true
+ verify(blurUtils).prepareBlur(any(), anyInt())
verify(choreographer)
.postFrameCallback(eq(notificationShadeDepthController.updateBlurCallback))
}
@@ -419,6 +420,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
verify(wallpaperController).setNotificationShadeZoom(eq(1f))
+ verify(blurUtils).prepareBlur(any(), eq(0))
verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index 529519a6246e..a50155628027 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -22,21 +22,31 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Color;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.ColorInt;
import androidx.test.filters.SmallTest;
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
+import com.android.internal.util.ContrastColorUtil;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -53,13 +63,25 @@ import java.util.ArrayList;
@TestableLooper.RunWithLooper
public class LightBarControllerTest extends SysuiTestCase {
+ private static final GradientColors COLORS_LIGHT = makeColors(Color.WHITE);
+ private static final GradientColors COLORS_DARK = makeColors(Color.BLACK);
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private LightBarTransitionsController mLightBarTransitionsController;
+ private LightBarTransitionsController mNavBarController;
private SysuiDarkIconDispatcher mStatusBarIconController;
private LightBarController mLightBarController;
+ /** Allow testing with NEW_LIGHT_BAR_LOGIC flag in different states */
+ protected boolean testNewLightBarLogic() {
+ return false;
+ }
+
@Before
public void setup() {
+ mFeatureFlags.set(Flags.NEW_LIGHT_BAR_LOGIC, testNewLightBarLogic());
mStatusBarIconController = mock(SysuiDarkIconDispatcher.class);
+ mNavBarController = mock(LightBarTransitionsController.class);
+ when(mNavBarController.supportsIconTintForNavMode(anyInt())).thenReturn(true);
mLightBarTransitionsController = mock(LightBarTransitionsController.class);
when(mStatusBarIconController.getTransitionsController()).thenReturn(
mLightBarTransitionsController);
@@ -68,10 +90,19 @@ public class LightBarControllerTest extends SysuiTestCase {
mStatusBarIconController,
mock(BatteryController.class),
mock(NavigationModeController.class),
+ mFeatureFlags,
mock(DumpManager.class),
new FakeDisplayTracker(mContext));
}
+ private static GradientColors makeColors(@ColorInt int bgColor) {
+ GradientColors colors = new GradientColors();
+ colors.setMainColor(bgColor);
+ colors.setSecondaryColor(bgColor);
+ colors.setSupportsDarkText(!ContrastColorUtil.isColorDark(bgColor));
+ return colors;
+ }
+
@Test
public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksLight() {
final Rect firstBounds = new Rect(0, 0, 1, 1);
@@ -177,4 +208,54 @@ public class LightBarControllerTest extends SysuiTestCase {
false /* navbarColorManagedByIme */);
verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
}
+
+ @Test
+ public void validateNavBarChangesUpdateIcons() {
+ assumeTrue(testNewLightBarLogic()); // Only run in the new suite
+
+ // On the launcher in dark mode buttons are light
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK);
+ mLightBarController.onNavigationBarAppearanceChanged(
+ 0, /* nbModeChanged = */ true,
+ MODE_TRANSPARENT, /* navbarColorManagedByIme = */ false);
+ verifyNavBarIconsUnchanged(); // no changes yet; not attached
+
+ // Initial state is set when controller is set
+ mLightBarController.setNavigationBar(mNavBarController);
+ verifyNavBarIconsDarkSetTo(false);
+
+ // Changing the color of the transparent scrim has no effect
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_LIGHT);
+ verifyNavBarIconsUnchanged(); // still light
+
+ // Showing the notification shade with white scrim requires dark icons
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 1f, COLORS_LIGHT);
+ verifyNavBarIconsDarkSetTo(true);
+
+ // Expanded QS always provides a black background, so icons become light again
+ mLightBarController.setQsExpanded(true);
+ verifyNavBarIconsDarkSetTo(false);
+
+ // Tapping the QS tile to change to dark theme has no effect in this state
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 1f, COLORS_DARK);
+ verifyNavBarIconsUnchanged(); // still light
+
+ // collapsing QS in dark mode doesn't affect button color
+ mLightBarController.setQsExpanded(false);
+ verifyNavBarIconsUnchanged(); // still light
+
+ // Closing the shade has no affect
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK);
+ verifyNavBarIconsUnchanged(); // still light
+ }
+
+ private void verifyNavBarIconsUnchanged() {
+ verify(mNavBarController, never()).setIconsDark(anyBoolean(), anyBoolean());
+ }
+
+ private void verifyNavBarIconsDarkSetTo(boolean iconsDark) {
+ verify(mNavBarController).setIconsDark(eq(iconsDark), anyBoolean());
+ verify(mNavBarController, never()).setIconsDark(eq(!iconsDark), anyBoolean());
+ clearInvocations(mNavBarController);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt
new file mode 100644
index 000000000000..d9c2cfa5ea7f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.flags.Flags.NEW_LIGHT_BAR_LOGIC
+
+/**
+ * This file only needs to live as long as [NEW_LIGHT_BAR_LOGIC] does. When we delete that flag, we
+ * can roll this back into the old test.
+ */
+@SmallTest
+class LightBarControllerWithNewLogicTest : LightBarControllerTest() {
+ override fun testNewLightBarLogic(): Boolean = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index b80b825d87dc..c282c1ef0cf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -21,6 +21,8 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
+import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
@@ -49,7 +51,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
fun calculateWidthFor_oneIcon_widthForOneIcon() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f),
/* actual= */ 30f)
@@ -59,7 +61,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
fun calculateWidthFor_fourIcons_widthForFourIcons() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f),
/* actual= */ 60f)
@@ -69,7 +71,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
fun calculateWidthFor_fiveIcons_widthForFourIcons() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f),
/* actual= */ 60f)
}
@@ -78,7 +80,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
val icon = mockStatusBarIcon()
iconContainer.addView(icon)
@@ -99,7 +101,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
val iconOne = mockStatusBarIcon()
val iconTwo = mockStatusBarIcon()
@@ -128,7 +130,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
val iconOne = mockStatusBarIcon()
val iconTwo = mockStatusBarIcon()
@@ -154,6 +156,55 @@ class NotificationIconContainerTest : SysuiTestCase() {
}
@Test
+ fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() {
+ iconContainer.setActualPaddingStart(0f)
+ iconContainer.setActualPaddingEnd(0f)
+ iconContainer.setActualLayoutWidth(30)
+ iconContainer.setIconSize(10)
+
+ val iconOne = mockStatusBarIcon()
+ val iconTwo = mockStatusBarIcon()
+ val iconThree = mockStatusBarIcon()
+
+ iconContainer.addView(iconOne)
+ iconContainer.addView(iconTwo)
+ iconContainer.addView(iconThree)
+ assertEquals(3, iconContainer.childCount)
+
+ iconContainer.calculateIconXTranslations()
+ assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation)
+ assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation)
+ assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation)
+ assertFalse(iconContainer.areIconsOverflowing())
+ }
+
+ @Test
+ fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() {
+ iconContainer.setActualPaddingStart(0f)
+ iconContainer.setActualPaddingEnd(0f)
+ iconContainer.setActualLayoutWidth(35)
+ iconContainer.setIconSize(10)
+
+ val iconOne = mockStatusBarIcon()
+ val iconTwo = mockStatusBarIcon()
+ val iconThree = mockStatusBarIcon()
+ val iconFour = mockStatusBarIcon()
+
+ iconContainer.addView(iconOne)
+ iconContainer.addView(iconTwo)
+ iconContainer.addView(iconThree)
+ iconContainer.addView(iconFour)
+ assertEquals(4, iconContainer.childCount)
+
+ iconContainer.calculateIconXTranslations()
+ assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation)
+ assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation)
+ assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState)
+ assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState)
+ assertTrue(iconContainer.areIconsOverflowing())
+ }
+
+ @Test
fun shouldForceOverflow_appearingAboveSpeedBump_true() {
val forceOverflow = iconContainer.shouldForceOverflow(
/* i= */ 1,
@@ -161,7 +212,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
/* iconAppearAmount= */ 1f,
/* maxVisibleIcons= */ 5
)
- assertTrue(forceOverflow);
+ assertTrue(forceOverflow)
}
@Test
@@ -172,7 +223,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
/* iconAppearAmount= */ 0f,
/* maxVisibleIcons= */ 5
)
- assertTrue(forceOverflow);
+ assertTrue(forceOverflow)
}
@Test
@@ -183,7 +234,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
/* iconAppearAmount= */ 0f,
/* maxVisibleIcons= */ 5
)
- assertFalse(forceOverflow);
+ assertFalse(forceOverflow)
}
@Test
@@ -210,6 +261,17 @@ class NotificationIconContainerTest : SysuiTestCase() {
}
@Test
+ fun isOverflowing_lastChildXGreaterThanDotX_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 9f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() {
val isOverflowing = iconContainer.isOverflowing(
/* isLastChild= */ true,
@@ -253,7 +315,7 @@ class NotificationIconContainerTest : SysuiTestCase() {
assertTrue(isOverflowing)
}
- private fun mockStatusBarIcon() : StatusBarIconView {
+ private fun mockStatusBarIcon(): StatusBarIconView {
val iconView = mock(StatusBarIconView::class.java)
whenever(iconView.width).thenReturn(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index db50163ce6a7..b8e4306a319d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.policy;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -125,4 +126,20 @@ public class CastControllerImplTest extends SysuiTestCase {
fail("Concurrent modification exception");
}
}
+
+ @Test
+ public void hasConnectedCastDevice_connected() {
+ CastController.CastDevice castDevice = new CastController.CastDevice();
+ castDevice.state = CastController.CastDevice.STATE_CONNECTED;
+ mController.startCasting(castDevice);
+ assertTrue(mController.hasConnectedCastDevice());
+ }
+
+ @Test
+ public void hasConnectedCastDevice_notConnected() {
+ CastController.CastDevice castDevice = new CastController.CastDevice();
+ castDevice.state = CastController.CastDevice.STATE_CONNECTING;
+ mController.startCasting(castDevice);
+ assertTrue(mController.hasConnectedCastDevice());
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt
new file mode 100644
index 000000000000..fd9139165c85
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.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.biometrics.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeRearDisplayStateRepository : RearDisplayStateRepository {
+ private val _isInRearDisplayMode = MutableStateFlow<Boolean>(false)
+ override val isInRearDisplayMode: StateFlow<Boolean> = _isInRearDisplayMode.asStateFlow()
+
+ fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) {
+ _isInRearDisplayMode.value = isInRearDisplayMode
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
index f6b24da9b821..84ace7cc9540 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -51,4 +51,9 @@ public class FakeCastController extends BaseLeakChecker<Callback> implements Cas
public void stopCasting(CastDevice device) {
}
+
+ @Override
+ public boolean hasConnectedCastDevice() {
+ return false;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a3a067481022..0172eaf03fa7 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -54,7 +54,6 @@ import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.bluetooth.BluetoothDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.DeviceNotAssociatedException;
@@ -108,6 +107,9 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
import com.android.server.companion.transport.CompanionTransportManager;
import com.android.server.pm.UserManagerInternal;
@@ -117,6 +119,7 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -200,6 +203,8 @@ public class CompanionDeviceManagerService extends SystemService {
private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
new RemoteCallbackList<>();
+ private CrossDeviceSyncController mCrossDeviceSyncController;
+
public CompanionDeviceManagerService(Context context) {
super(context);
@@ -229,7 +234,7 @@ public class CompanionDeviceManagerService extends SystemService {
loadAssociationsFromDisk();
mAssociationStore.registerListener(mAssociationStoreChangeListener);
- mDevicePresenceMonitor = new CompanionDevicePresenceMonitor(mUserManager,
+ mDevicePresenceMonitor = new CompanionDevicePresenceMonitor(
mAssociationStore, mDevicePresenceCallback);
mAssociationRequestsProcessor = new AssociationRequestsProcessor(
@@ -239,6 +244,8 @@ public class CompanionDeviceManagerService extends SystemService {
mTransportManager = new CompanionTransportManager(context, mAssociationStore);
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
+ // TODO(b/279663946): move context sync to a dedicated system service
+ mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager);
// Publish "binder" service.
final CompanionDeviceManagerImpl impl = new CompanionDeviceManagerImpl();
@@ -315,21 +322,6 @@ public class CompanionDeviceManagerService extends SystemService {
MINUTES.toMillis(10));
}
- @Override
- public void onUserUnlocked(@NonNull TargetUser user) {
- // Notify and bind the app after the phone is unlocked.
- final int userId = user.getUserIdentifier();
- final Set<BluetoothDevice> blueToothDevices =
- mDevicePresenceMonitor.getPendingReportConnectedDevices().get(userId);
- for (BluetoothDevice bluetoothDevice : blueToothDevices) {
- for (AssociationInfo ai:
- mAssociationStore.getAssociationsByAddress(bluetoothDevice.getAddress())) {
- Slog.i(TAG, "onUserUnlocked, device id( " + ai.getId() + " ) is connected");
- mDevicePresenceMonitor.onBluetoothCompanionDeviceConnected(ai.getId());
- }
- }
- }
-
@NonNull
AssociationInfo getAssociationWithCallerChecks(
@UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
@@ -1369,6 +1361,39 @@ public class CompanionDeviceManagerService extends SystemService {
public void removeInactiveSelfManagedAssociations() {
CompanionDeviceManagerService.this.removeInactiveSelfManagedAssociations();
}
+
+ @Override
+ public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.registerCallMetadataSyncCallback(callback);
+ }
+ }
+
+ @Override
+ public void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.syncToAllDevicesForUserId(userId, calls);
+ }
+ }
+
+ @Override
+ public void crossDeviceSync(AssociationInfo associationInfo,
+ Collection<CrossDeviceCall> calls) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.syncToSingleDevice(associationInfo, calls);
+ }
+ }
+
+ @Override
+ public void sendCrossDeviceSyncMessage(int associationId, byte[] message) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.syncMessageToDevice(associationId, message);
+ }
+ }
}
/**
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
index 36492407d463..3b108e63e13d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
@@ -16,12 +16,41 @@
package com.android.server.companion;
+import android.companion.AssociationInfo;
+
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback;
+
+import java.util.Collection;
+
/**
* Companion Device Manager Local System Service Interface.
*/
-interface CompanionDeviceManagerServiceInternal {
+public interface CompanionDeviceManagerServiceInternal {
/**
* @see CompanionDeviceManagerService#removeInactiveSelfManagedAssociations
*/
void removeInactiveSelfManagedAssociations();
+
+ /**
+ * Registers a callback from an InCallService / ConnectionService to CDM to process sync
+ * requests and perform call control actions.
+ */
+ void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback);
+
+ /**
+ * Requests a sync from an InCallService / ConnectionService to CDM, for the given association
+ * and message.
+ */
+ void sendCrossDeviceSyncMessage(int associationId, byte[] message);
+
+ /**
+ * Requests a sync from an InCallService to CDM, for the given user and call metadata.
+ */
+ void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls);
+
+ /**
+ * Requests a sync from an InCallService to CDM, for the given association and call metadata.
+ */
+ void crossDeviceSync(AssociationInfo associationInfo, Collection<CrossDeviceCall> calls);
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
index ae4766ac9fda..443a732eb6f1 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
@@ -17,15 +17,20 @@
package com.android.server.companion.datatransfer.contextsync;
import android.annotation.Nullable;
+import android.companion.AssociationInfo;
import android.telecom.Call;
import android.telecom.InCallService;
import android.telecom.TelecomManager;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.companion.CompanionDeviceConfig;
+import com.android.server.companion.CompanionDeviceManagerServiceInternal;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.stream.Collectors;
@@ -35,90 +40,132 @@ import java.util.stream.Collectors;
*/
public class CallMetadataSyncInCallService extends InCallService {
+ private static final String TAG = "CallMetadataIcs";
private static final long NOT_VALID = -1L;
+ private CompanionDeviceManagerServiceInternal mCdmsi;
+
@VisibleForTesting
final Map<Call, CrossDeviceCall> mCurrentCalls = new HashMap<>();
- @VisibleForTesting
- boolean mShouldSync;
+ @VisibleForTesting int mNumberOfActiveSyncAssociations;
final Call.Callback mTelecomCallback = new Call.Callback() {
@Override
public void onDetailsChanged(Call call, Call.Details details) {
- mCurrentCalls.get(call).updateCallDetails(details);
+ if (mNumberOfActiveSyncAssociations > 0) {
+ final CrossDeviceCall crossDeviceCall = mCurrentCalls.get(call);
+ if (crossDeviceCall != null) {
+ crossDeviceCall.updateCallDetails(details);
+ sync(getUserId());
+ } else {
+ Slog.w(TAG, "Could not update details for nonexistent call");
+ }
+ }
}
};
- final CallMetadataSyncCallback mCallMetadataSyncCallback = new CallMetadataSyncCallback() {
- @Override
- void processCallControlAction(int crossDeviceCallId, int callControlAction) {
- final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
- mCurrentCalls.values());
- switch (callControlAction) {
- case android.companion.Telecom.Call.ACCEPT:
- if (crossDeviceCall != null) {
- crossDeviceCall.doAccept();
- }
- break;
- case android.companion.Telecom.Call.REJECT:
- if (crossDeviceCall != null) {
- crossDeviceCall.doReject();
+ final CrossDeviceSyncControllerCallback
+ mCrossDeviceSyncControllerCallback = new CrossDeviceSyncControllerCallback() {
+ @Override
+ void processContextSyncMessage(int associationId,
+ CallMetadataSyncData callMetadataSyncData) {
+ final Iterator<CallMetadataSyncData.Call> iterator =
+ callMetadataSyncData.getRequests().iterator();
+ while (iterator.hasNext()) {
+ final CallMetadataSyncData.Call call = iterator.next();
+ if (call.getId() != 0) {
+ // The call is already assigned an id; treat as control invocations.
+ for (int control : call.getControls()) {
+ processCallControlAction(call.getId(), control);
+ }
+ }
+ iterator.remove();
}
- break;
- case android.companion.Telecom.Call.SILENCE:
- doSilence();
- break;
- case android.companion.Telecom.Call.MUTE:
- doMute();
- break;
- case android.companion.Telecom.Call.UNMUTE:
- doUnmute();
- break;
- case android.companion.Telecom.Call.END:
- if (crossDeviceCall != null) {
- crossDeviceCall.doEnd();
- }
- break;
- case android.companion.Telecom.Call.PUT_ON_HOLD:
- if (crossDeviceCall != null) {
- crossDeviceCall.doPutOnHold();
- }
- break;
- case android.companion.Telecom.Call.TAKE_OFF_HOLD:
- if (crossDeviceCall != null) {
- crossDeviceCall.doTakeOffHold();
+ }
+
+ private void processCallControlAction(long crossDeviceCallId,
+ int callControlAction) {
+ final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
+ mCurrentCalls.values());
+ switch (callControlAction) {
+ case android.companion.Telecom.Call.ACCEPT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doAccept();
+ }
+ break;
+ case android.companion.Telecom.Call.REJECT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doReject();
+ }
+ break;
+ case android.companion.Telecom.Call.SILENCE:
+ doSilence();
+ break;
+ case android.companion.Telecom.Call.MUTE:
+ doMute();
+ break;
+ case android.companion.Telecom.Call.UNMUTE:
+ doUnmute();
+ break;
+ case android.companion.Telecom.Call.END:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doEnd();
+ }
+ break;
+ case android.companion.Telecom.Call.PUT_ON_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doPutOnHold();
+ }
+ break;
+ case android.companion.Telecom.Call.TAKE_OFF_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doTakeOffHold();
+ }
+ break;
+ default:
}
- break;
- default:
- }
- }
+ }
- @Override
- void requestCrossDeviceSync(int userId) {
- }
+ @Override
+ void requestCrossDeviceSync(AssociationInfo associationInfo) {
+ if (associationInfo.getUserId() == getUserId()) {
+ sync(associationInfo);
+ }
+ }
- @Override
- void updateStatus(int userId, boolean shouldSyncCallMetadata) {
- if (userId == getUserId()) {
- mShouldSync = shouldSyncCallMetadata;
- if (shouldSyncCallMetadata) {
- initializeCalls();
- } else {
- mCurrentCalls.clear();
+ @Override
+ void updateNumberOfActiveSyncAssociations(int userId, boolean added) {
+ if (userId == getUserId()) {
+ final boolean wasActivelySyncing = mNumberOfActiveSyncAssociations > 0;
+ if (added) {
+ mNumberOfActiveSyncAssociations++;
+ } else {
+ mNumberOfActiveSyncAssociations--;
+ }
+ if (!wasActivelySyncing && mNumberOfActiveSyncAssociations > 0) {
+ initializeCalls();
+ } else if (wasActivelySyncing && mNumberOfActiveSyncAssociations <= 0) {
+ mCurrentCalls.clear();
+ }
+ }
}
- }
- }
};
@Override
public void onCreate() {
super.onCreate();
- initializeCalls();
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class);
+ mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback);
+ }
}
private void initializeCalls() {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call,
call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState()))));
+ mCurrentCalls.keySet().forEach(call -> call.registerCallback(mTelecomCallback,
+ getMainThreadHandler()));
+ sync(getUserId());
}
}
@@ -139,33 +186,39 @@ public class CallMetadataSyncInCallService extends InCallService {
@Override
public void onCallAdded(Call call) {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.put(call,
new CrossDeviceCall(getPackageManager(), call, getCallAudioState()));
+ call.registerCallback(mTelecomCallback);
+ sync(getUserId());
}
}
@Override
public void onCallRemoved(Call call) {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.remove(call);
+ call.unregisterCallback(mTelecomCallback);
+ sync(getUserId());
}
}
@Override
public void onMuteStateChanged(boolean isMuted) {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.values().forEach(call -> call.updateMuted(isMuted));
+ sync(getUserId());
}
}
@Override
public void onSilenceRinger() {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.values().forEach(call -> call.updateSilencedIfRinging());
+ sync(getUserId());
}
}
@@ -183,4 +236,12 @@ public class CallMetadataSyncInCallService extends InCallService {
telecomManager.silenceRinger();
}
}
+
+ private void sync(int userId) {
+ mCdmsi.crossDeviceSync(userId, mCurrentCalls.values());
+ }
+
+ private void sync(AssociationInfo associationInfo) {
+ mCdmsi.crossDeviceSync(associationInfo, mCurrentCalls.values());
+ }
} \ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
index 3d8fb7a8d5bf..adc5faf24f2c 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
@@ -16,27 +16,32 @@
package com.android.server.companion.datatransfer.contextsync;
+import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_CONTEXT_SYNC;
+
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
+import android.companion.IOnMessageReceivedListener;
+import android.companion.IOnTransportsChangedListener;
import android.companion.Telecom;
-import android.companion.Telecom.Call;
import android.content.Context;
+import android.os.Binder;
import android.os.UserHandle;
-import android.util.Pair;
import android.util.Slog;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoParseException;
+import android.util.proto.ProtoUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.companion.CompanionDeviceConfig;
+import com.android.server.companion.transport.CompanionTransportManager;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -45,149 +50,308 @@ import java.util.Set;
public class CrossDeviceSyncController {
private static final String TAG = "CrossDeviceSyncController";
- private static final int BYTE_ARRAY_SIZE = 64;
+
+ private static final int VERSION_1 = 1;
+ private static final int CURRENT_VERSION = VERSION_1;
private final Context mContext;
- private final Callback mCdmCallback;
- private final Map<Integer, List<AssociationInfo>> mUserIdToAssociationInfo = new HashMap<>();
- private final Map<Integer, Pair<InputStream, OutputStream>> mAssociationIdToStreams =
- new HashMap<>();
+ private final CompanionTransportManager mCompanionTransportManager;
+ private final List<AssociationInfo> mConnectedAssociations = new ArrayList<>();
private final Set<Integer> mBlocklist = new HashSet<>();
- private CallMetadataSyncCallback mInCallServiceCallMetadataSyncCallback;
+ private CrossDeviceSyncControllerCallback mCrossDeviceSyncControllerCallback;
- public CrossDeviceSyncController(Context context, Callback callback) {
+ public CrossDeviceSyncController(Context context,
+ CompanionTransportManager companionTransportManager) {
mContext = context;
- mCdmCallback = callback;
+ mCompanionTransportManager = companionTransportManager;
+ mCompanionTransportManager.addListener(new IOnTransportsChangedListener.Stub() {
+ @Override
+ public void onTransportsChanged(List<AssociationInfo> newAssociations) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ final List<AssociationInfo> existingAssociations = new ArrayList<>(
+ mConnectedAssociations);
+ mConnectedAssociations.clear();
+ mConnectedAssociations.addAll(newAssociations);
+
+ if (mCrossDeviceSyncControllerCallback == null) {
+ Slog.w(TAG, "No callback to report transports changed");
+ return;
+ }
+ for (AssociationInfo associationInfo : newAssociations) {
+ if (!existingAssociations.contains(associationInfo)
+ && !isAssociationBlocked(associationInfo.getId())) {
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ true);
+ mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
+ }
+ }
+ for (AssociationInfo associationInfo : existingAssociations) {
+ if (!newAssociations.contains(associationInfo)) {
+ if (isAssociationBlocked(associationInfo.getId())) {
+ mBlocklist.remove(associationInfo.getId());
+ } else {
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ false);
+ }
+ }
+ }
+ }
+ });
+ mCompanionTransportManager.addListener(MESSAGE_REQUEST_CONTEXT_SYNC,
+ new IOnMessageReceivedListener.Stub() {
+ @Override
+ public void onMessageReceived(int associationId, byte[] data) {
+ if (mCrossDeviceSyncControllerCallback == null) {
+ Slog.w(TAG, "No callback to process context sync message");
+ return;
+ }
+ mCrossDeviceSyncControllerCallback.processContextSyncMessage(associationId,
+ processTelecomDataFromSync(data));
+ }
+ });
+ }
+
+ private boolean isAssociationBlocked(int associationId) {
+ return mBlocklist.contains(associationId);
}
/** Registers the call metadata callback. */
- public void registerCallMetadataSyncCallback(CallMetadataSyncCallback callback) {
- mInCallServiceCallMetadataSyncCallback = callback;
+ public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
+ mCrossDeviceSyncControllerCallback = callback;
+ for (AssociationInfo associationInfo : mConnectedAssociations) {
+ if (!isAssociationBlocked(associationInfo.getId())) {
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ true);
+ mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
+ }
+ }
}
/** Allow specific associated devices to enable / disable syncing. */
public void setSyncEnabled(AssociationInfo associationInfo, boolean enabled) {
if (enabled) {
- if (mBlocklist.contains(associationInfo.getId())) {
+ if (isAssociationBlocked(associationInfo.getId())) {
mBlocklist.remove(associationInfo.getId());
- openChannel(associationInfo);
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ true);
+ mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
}
} else {
- if (!mBlocklist.contains(associationInfo.getId())) {
+ if (!isAssociationBlocked(associationInfo.getId())) {
mBlocklist.add(associationInfo.getId());
- closeChannel(associationInfo);
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ false);
+ // Send empty message to device to clear its data (otherwise it will get stale)
+ syncMessageToDevice(associationInfo.getId(), createEmptyMessage());
}
}
}
+ private boolean isAdminBlocked(int userId) {
+ return mContext.getSystemService(DevicePolicyManager.class)
+ .getBluetoothContactSharingDisabled(UserHandle.of(userId));
+ }
+
/**
- * Opens channels to newly associated devices, and closes channels to newly disassociated
- * devices.
+ * Sync data to associated devices.
*
- * TODO(b/265466098): this needs to be limited to just connected devices
+ * @param userId The user whose data should be synced.
+ * @param calls The full list of current calls for all users.
*/
- public void onAssociationsChanged(int userId, List<AssociationInfo> newAssociationInfoList) {
- final List<AssociationInfo> existingAssociationInfoList = mUserIdToAssociationInfo.get(
- userId);
- // Close channels to newly-disconnected devices.
- for (AssociationInfo existingAssociationInfo : existingAssociationInfoList) {
- if (!newAssociationInfoList.contains(existingAssociationInfo) && !mBlocklist.contains(
- existingAssociationInfo.getId())) {
- closeChannel(existingAssociationInfo);
+ public void syncToAllDevicesForUserId(int userId, Collection<CrossDeviceCall> calls) {
+ final Set<Integer> associationIds = new HashSet<>();
+ for (AssociationInfo associationInfo : mConnectedAssociations) {
+ if (associationInfo.getUserId() == userId && !isAssociationBlocked(
+ associationInfo.getId())) {
+ associationIds.add(associationInfo.getId());
}
}
- // Open channels to newly-connected devices.
- for (AssociationInfo newAssociationInfo : newAssociationInfoList) {
- if (!existingAssociationInfoList.contains(newAssociationInfo) && !mBlocklist.contains(
- newAssociationInfo.getId())) {
- openChannel(newAssociationInfo);
- }
+ if (associationIds.isEmpty()) {
+ Slog.w(TAG, "No eligible devices to sync to");
+ return;
}
- mUserIdToAssociationInfo.put(userId, newAssociationInfoList);
- }
- private boolean isAdminBlocked(int userId) {
- return mContext.getSystemService(DevicePolicyManager.class)
- .getBluetoothContactSharingDisabled(UserHandle.of(userId));
+ mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC,
+ createCallUpdateMessage(calls, userId),
+ associationIds.stream().mapToInt(Integer::intValue).toArray());
}
- /** Stop reading, close streams, and close secure channel. */
- private void closeChannel(AssociationInfo associationInfo) {
- // TODO(b/265466098): stop reading from secure channel
- final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get(
- associationInfo.getId());
- if (streams != null) {
- try {
- if (streams.first != null) {
- streams.first.close();
- }
- if (streams.second != null) {
- streams.second.close();
- }
- } catch (IOException e) {
- Slog.e(TAG, "Could not close streams for association " + associationInfo.getId(),
- e);
- }
+ /**
+ * Sync data to associated devices.
+ *
+ * @param associationInfo The association whose data should be synced.
+ * @param calls The full list of current calls for all users.
+ */
+ public void syncToSingleDevice(AssociationInfo associationInfo,
+ Collection<CrossDeviceCall> calls) {
+ if (isAssociationBlocked(associationInfo.getId())) {
+ Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
+ return;
}
- mCdmCallback.closeSecureChannel(associationInfo.getId());
- }
- /** Sync initial snapshot and start reading. */
- private void openChannel(AssociationInfo associationInfo) {
- final InputStream is = new ByteArrayInputStream(new byte[BYTE_ARRAY_SIZE]);
- final OutputStream os = new ByteArrayOutputStream(BYTE_ARRAY_SIZE);
- mAssociationIdToStreams.put(associationInfo.getId(), new Pair<>(is, os));
- mCdmCallback.createSecureChannel(associationInfo.getId(), is, os);
- // TODO(b/265466098): only requestSync for this specific association / connection?
- mInCallServiceCallMetadataSyncCallback.requestCrossDeviceSync(associationInfo.getUserId());
- // TODO(b/265466098): start reading from secure channel
+ mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC,
+ createCallUpdateMessage(calls, associationInfo.getUserId()),
+ new int[]{associationInfo.getId()});
}
/**
* Sync data to associated devices.
*
- * @param userId The user whose data should be synced.
- * @param calls The full list of current calls for all users.
+ * @param associationId The association whose data should be synced.
+ * @param message The message to sync.
*/
- public void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls) {
- final boolean isAdminBlocked = isAdminBlocked(userId);
- for (AssociationInfo associationInfo : mUserIdToAssociationInfo.get(userId)) {
- final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get(
- associationInfo.getId());
- final ProtoOutputStream pos = new ProtoOutputStream(streams.second);
- final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
- for (CrossDeviceCall call : calls) {
- final long callsToken = pos.start(Telecom.CALLS);
- pos.write(Call.ID, call.getId());
- final long originToken = pos.start(Call.ORIGIN);
- pos.write(Call.Origin.CALLER_ID, call.getReadableCallerId(isAdminBlocked));
- pos.write(Call.Origin.APP_ICON, call.getCallingAppIcon());
- pos.write(Call.Origin.APP_NAME, call.getCallingAppName());
- pos.end(originToken);
- pos.write(Call.STATUS, call.getStatus());
- for (int control : call.getControls()) {
- pos.write(Call.CONTROLS_AVAILABLE, control);
+ public void syncMessageToDevice(int associationId, byte[] message) {
+ if (isAssociationBlocked(associationId)) {
+ Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
+ return;
+ }
+
+ mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC, message,
+ new int[]{associationId});
+ }
+
+ @VisibleForTesting
+ CallMetadataSyncData processTelecomDataFromSync(byte[] data) {
+ final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
+ final ProtoInputStream pis = new ProtoInputStream(data);
+ try {
+ int version = -1;
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) ContextSyncMessage.VERSION:
+ version = pis.readInt(ContextSyncMessage.VERSION);
+ Slog.e(TAG, "Processing context sync message version " + version);
+ break;
+ case (int) ContextSyncMessage.TELECOM:
+ if (version == VERSION_1) {
+ final long telecomToken = pis.start(ContextSyncMessage.TELECOM);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) Telecom.CALLS) {
+ final long callsToken = pis.start(Telecom.CALLS);
+ callMetadataSyncData.addCall(processCallDataFromSync(pis));
+ pis.end(callsToken);
+ } else if (pis.getFieldNumber() == (int) Telecom.REQUESTS) {
+ final long requestsToken = pis.start(Telecom.REQUESTS);
+ callMetadataSyncData.addRequest(processCallDataFromSync(pis));
+ pis.end(requestsToken);
+ } else {
+ Slog.e(TAG, "Unhandled field in Telecom:"
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ pis.end(telecomToken);
+ } else {
+ Slog.e(TAG, "Cannot process unsupported version " + version);
+ }
+ break;
+ default:
+ Slog.e(TAG, "Unhandled field in ContextSyncMessage:"
+ + ProtoUtils.currentFieldToString(pis));
}
- pos.end(callsToken);
}
- pos.end(telecomToken);
- pos.flush();
+ } catch (IOException | ProtoParseException e) {
+ throw new RuntimeException(e);
}
+ return callMetadataSyncData;
}
- /**
- * Callback to be implemented by CompanionDeviceManagerService.
- */
- public interface Callback {
- /**
- * Create a secure channel to send messages.
- */
- void createSecureChannel(int associationId, InputStream input, OutputStream output);
-
- /**
- * Close the secure channel created previously.
- */
- void closeSecureChannel(int associationId);
+ @VisibleForTesting
+ CallMetadataSyncData.Call processCallDataFromSync(ProtoInputStream pis) throws IOException {
+ final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) Telecom.Call.ID:
+ call.setId(pis.readLong(Telecom.Call.ID));
+ break;
+ case (int) Telecom.Call.ORIGIN:
+ final long originToken = pis.start(Telecom.Call.ORIGIN);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) Telecom.Call.Origin.APP_ICON:
+ call.setAppIcon(pis.readBytes(Telecom.Call.Origin.APP_ICON));
+ break;
+ case (int) Telecom.Call.Origin.APP_NAME:
+ call.setAppName(pis.readString(Telecom.Call.Origin.APP_NAME));
+ break;
+ case (int) Telecom.Call.Origin.CALLER_ID:
+ call.setCallerId(pis.readString(Telecom.Call.Origin.CALLER_ID));
+ break;
+ case (int) Telecom.Call.Origin.APP_IDENTIFIER:
+ call.setAppIdentifier(
+ pis.readString(Telecom.Call.Origin.APP_IDENTIFIER));
+ break;
+ default:
+ Slog.e(TAG, "Unhandled field in Origin:"
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ pis.end(originToken);
+ break;
+ case (int) Telecom.Call.STATUS:
+ call.setStatus(pis.readInt(Telecom.Call.STATUS));
+ break;
+ case (int) Telecom.Call.CONTROLS:
+ call.addControl(pis.readInt(Telecom.Call.CONTROLS));
+ break;
+ default:
+ Slog.e(TAG,
+ "Unhandled field in Telecom:" + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ return call;
+ }
+
+ @VisibleForTesting
+ byte[] createCallUpdateMessage(Collection<CrossDeviceCall> calls, int userId) {
+ final ProtoOutputStream pos = new ProtoOutputStream();
+ pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
+ final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
+ for (CrossDeviceCall call : calls) {
+ final long callsToken = pos.start(Telecom.CALLS);
+ pos.write(Telecom.Call.ID, call.getId());
+ final long originToken = pos.start(Telecom.Call.ORIGIN);
+ pos.write(Telecom.Call.Origin.CALLER_ID,
+ call.getReadableCallerId(isAdminBlocked(userId)));
+ pos.write(Telecom.Call.Origin.APP_ICON, call.getCallingAppIcon());
+ pos.write(Telecom.Call.Origin.APP_NAME, call.getCallingAppName());
+ pos.write(Telecom.Call.Origin.APP_IDENTIFIER, call.getCallingAppPackageName());
+ pos.end(originToken);
+ pos.write(Telecom.Call.STATUS, call.getStatus());
+ for (int control : call.getControls()) {
+ pos.write(Telecom.Call.CONTROLS, control);
+ }
+ pos.end(callsToken);
+ }
+ pos.end(telecomToken);
+ return pos.getBytes();
+ }
+
+ /** Create a call control message. */
+ public static byte[] createCallControlMessage(long callId, int control) {
+ final ProtoOutputStream pos = new ProtoOutputStream();
+ pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
+ final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
+ final long requestsToken = pos.start(Telecom.REQUESTS);
+ pos.write(Telecom.Call.ID, callId);
+ pos.write(Telecom.Call.CONTROLS, control);
+ pos.end(requestsToken);
+ pos.end(telecomToken);
+ return pos.getBytes();
+ }
+
+ /** Create an empty context sync message, used to clear state. */
+ public static byte[] createEmptyMessage() {
+ final ProtoOutputStream pos = new ProtoOutputStream();
+ pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
+ return pos.getBytes();
}
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
index 7c339d213483..31e10a814568 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
@@ -16,12 +16,14 @@
package com.android.server.companion.datatransfer.contextsync;
+import android.companion.AssociationInfo;
+
/** Callback for call metadata syncing. */
-public abstract class CallMetadataSyncCallback {
+public abstract class CrossDeviceSyncControllerCallback {
- abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
+ void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) {}
- abstract void requestCrossDeviceSync(int userId);
+ void requestCrossDeviceSync(AssociationInfo associationInfo) {}
- abstract void updateStatus(int userId, boolean shouldSyncCallMetadata);
+ void updateNumberOfActiveSyncAssociations(int userId, boolean added) {}
}
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index 82d0325cd749..f6b99b551ecb 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -27,23 +27,17 @@ import android.companion.AssociationInfo;
import android.net.MacAddress;
import android.os.Handler;
import android.os.HandlerExecutor;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.util.Log;
-import android.util.SparseArray;
-import com.android.internal.annotations.GuardedBy;
import com.android.server.companion.AssociationStore;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
@SuppressLint("LongLogTag")
-public class BluetoothCompanionDeviceConnectionListener
+class BluetoothCompanionDeviceConnectionListener
extends BluetoothAdapter.BluetoothConnectionCallback
implements AssociationStore.OnChangeListener {
private static final String TAG = "CDM_BluetoothCompanionDeviceConnectionListener";
@@ -54,23 +48,15 @@ public class BluetoothCompanionDeviceConnectionListener
void onBluetoothCompanionDeviceDisconnected(int associationId);
}
- private final UserManager mUserManager;
private final @NonNull AssociationStore mAssociationStore;
private final @NonNull Callback mCallback;
/** A set of ALL connected BT device (not only companion.) */
private final @NonNull Map<MacAddress, BluetoothDevice> mAllConnectedDevices = new HashMap<>();
-
- @GuardedBy("mPendingReportConnectedDevices")
- @NonNull
- final SparseArray<Set<BluetoothDevice>> mPendingReportConnectedDevices =
- new SparseArray<>();
-
- BluetoothCompanionDeviceConnectionListener(UserManager userManager,
- @NonNull AssociationStore associationStore, @NonNull Callback callback) {
+ BluetoothCompanionDeviceConnectionListener(@NonNull AssociationStore associationStore,
+ @NonNull Callback callback) {
mAssociationStore = associationStore;
mCallback = callback;
- mUserManager = userManager;
}
public void init(@NonNull BluetoothAdapter btAdapter) {
@@ -90,32 +76,12 @@ public class BluetoothCompanionDeviceConnectionListener
if (DEBUG) Log.i(TAG, "onDevice_Connected() " + btDeviceToString(device));
final MacAddress macAddress = MacAddress.fromString(device.getAddress());
- final int userId = UserHandle.myUserId();
-
if (mAllConnectedDevices.put(macAddress, device) != null) {
if (DEBUG) Log.w(TAG, "Device " + btDeviceToString(device) + " is already connected.");
return;
}
- // Try to bind and notify the app after the phone is unlocked.
- if (!mUserManager.isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
- if (DEBUG) {
- Log.i(TAG, "Current user is not in unlocking or unlocked stage yet. Notify "
- + "the application when the phone is unlocked");
- }
- synchronized (mPendingReportConnectedDevices) {
- Set<BluetoothDevice> bluetoothDevices = mPendingReportConnectedDevices.get(userId);
-
- if (bluetoothDevices == null) {
- bluetoothDevices = new HashSet<>();
- mPendingReportConnectedDevices.put(userId, bluetoothDevices);
- }
-
- bluetoothDevices.add(device);
- }
- } else {
- onDeviceConnectivityChanged(device, true);
- }
+ onDeviceConnectivityChanged(device, true);
}
/**
@@ -132,8 +98,6 @@ public class BluetoothCompanionDeviceConnectionListener
}
final MacAddress macAddress = MacAddress.fromString(device.getAddress());
- final int userId = UserHandle.myUserId();
-
if (mAllConnectedDevices.remove(macAddress) == null) {
if (DEBUG) {
Log.w(TAG, "The device wasn't tracked as connected " + btDeviceToString(device));
@@ -141,19 +105,6 @@ public class BluetoothCompanionDeviceConnectionListener
return;
}
- // Do not need to report the connectivity since the user is not unlock the phone so
- // that cdm is not bind with the app yet.
- if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
- synchronized (mPendingReportConnectedDevices) {
- Set<BluetoothDevice> bluetoothDevices = mPendingReportConnectedDevices.get(userId);
- if (bluetoothDevices != null) {
- bluetoothDevices.remove(device);
- }
- }
-
- return;
- }
-
onDeviceConnectivityChanged(device, false);
}
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 6c565004d948..4010be922b2c 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -23,16 +23,13 @@ import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
import android.companion.AssociationInfo;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.UserManager;
import android.util.Log;
-import android.util.SparseArray;
import com.android.server.companion.AssociationStore;
@@ -89,12 +86,13 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper =
new SimulatedDevicePresenceSchedulerHelper();
- public CompanionDevicePresenceMonitor(UserManager userManager,
- @NonNull AssociationStore associationStore, @NonNull Callback callback) {
+ public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore,
+ @NonNull Callback callback) {
mAssociationStore = associationStore;
mCallback = callback;
- mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(userManager,
- associationStore, /* BluetoothCompanionDeviceConnectionListener.Callback */ this);
+
+ mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(associationStore,
+ /* BluetoothCompanionDeviceConnectionListener.Callback */ this);
mBleScanner = new BleCompanionDeviceScanner(associationStore,
/* BleCompanionDeviceScanner.Callback */ this);
}
@@ -300,15 +298,6 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
// what's needed.
}
- /**
- * Return a set of devices that pending to report connectivity
- */
- public SparseArray<Set<BluetoothDevice>> getPendingReportConnectedDevices() {
- synchronized (mBtConnectionListener.mPendingReportConnectedDevices) {
- return mBtConnectionListener.mPendingReportConnectedDevices;
- }
- }
-
private static void enforceCallerShellOrRoot() {
final int callingUid = Binder.getCallingUid();
if (callingUid == SHELL_UID || callingUid == ROOT_UID) return;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 678d58232f15..cf09fde9ec2c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -762,6 +762,15 @@ public final class ActiveServices {
}
}
+ private static void traceInstant(@NonNull String message, @NonNull ServiceRecord service) {
+ if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ return;
+ }
+ final String serviceName = (service.getComponentName() != null)
+ ? service.getComponentName().toShortString() : "(?)";
+ Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, message + serviceName);
+ }
+
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, final int userId, boolean isSdkSandboxService,
@@ -818,6 +827,9 @@ public final class ActiveServices {
}
ServiceRecord r = res.record;
+
+ traceInstant("startService(): ", r);
+
// Note, when startService() or startForegroundService() is called on an already
// running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw
// ForegroundServiceStartNotAllowedException), even when the service is already timed
@@ -1407,6 +1419,7 @@ public final class ActiveServices {
}
private void stopServiceLocked(ServiceRecord service, boolean enqueueOomAdj) {
+ traceInstant("stopService(): ", service);
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "stopServiceLocked()");
if (service.delayed) {
@@ -1946,6 +1959,7 @@ public final class ActiveServices {
if (notification == null) {
throw new IllegalArgumentException("null notification");
}
+ traceInstant("startForeground(): ", r);
final int foregroundServiceStartType = foregroundServiceType;
// Instant apps need permission to create foreground services.
if (r.appInfo.isInstantApp()) {
@@ -2484,6 +2498,7 @@ public final class ActiveServices {
}
} else {
if (r.isForeground) {
+ traceInstant("stopForeground(): ", r);
final ServiceMap smap = getServiceMapLocked(r.userId);
if (smap != null) {
decActiveForegroundAppLocked(smap, r);
@@ -3311,6 +3326,7 @@ public final class ActiveServices {
Slog.i(TAG_SERVICE, "Short FGS started: " + sr);
}
}
+ traceInstant("short FGS start/extend: ", sr);
sr.setShortFgsInfo(SystemClock.uptimeMillis());
// We'll restart the timeout.
@@ -3356,10 +3372,11 @@ public final class ActiveServices {
return;
}
Slog.e(TAG_SERVICE, "Short FGS timed out: " + sr);
- final long now = SystemClock.uptimeMillis();
+ traceInstant("short FGS timeout: ", sr);
+
logFGSStateChangeLocked(sr,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
- now > sr.mFgsEnterTime ? (int) (now - sr.mFgsEnterTime) : 0,
+ nowUptime > sr.mFgsEnterTime ? (int) (nowUptime - sr.mFgsEnterTime) : 0,
FGS_STOP_REASON_UNKNOWN,
FGS_TYPE_POLICY_CHECK_UNKNOWN);
try {
@@ -3410,6 +3427,7 @@ public final class ActiveServices {
}
Slog.e(TAG_SERVICE, "Short FGS procstate demoted: " + sr);
+ traceInstant("short FGS demote: ", sr);
mAm.updateOomAdjLocked(sr.app, OOM_ADJ_REASON_SHORT_FGS_TIMEOUT);
}
@@ -3440,6 +3458,9 @@ public final class ActiveServices {
} else {
Slog.e(TAG_SERVICE, message);
}
+
+ traceInstant("short FGS ANR: ", sr);
+
mAm.appNotResponding(sr.app, tr);
// TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a54e8e95b155..ea8745dc666d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6915,7 +6915,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mActivityTaskManager.unhandledBack();
}
- // TODO: Move to ContentProviderHelper?
+ // TODO: Replace this method with one that returns a bound IContentProvider.
public ParcelFileDescriptor openContentUri(String uriString) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
final int userId = UserHandle.getCallingUserId();
@@ -6944,6 +6944,16 @@ public class ActivityManagerService extends IActivityManager.Stub
Log.e(TAG, "Cannot find package for uid: " + uid);
return null;
}
+
+ final ApplicationInfo appInfo = mPackageManagerInt.getApplicationInfo(
+ androidPackage.getPackageName(), /*flags*/0, Process.SYSTEM_UID,
+ UserHandle.USER_SYSTEM);
+ if (!appInfo.isVendor() && !appInfo.isSystemApp() && !appInfo.isSystemExt()
+ && !appInfo.isProduct()) {
+ Log.e(TAG, "openContentUri may only be used by vendor/system/product.");
+ return null;
+ }
+
final AttributionSource attributionSource = new AttributionSource(
Binder.getCallingUid(), androidPackage.getPackageName(), null);
pfd = cph.provider.openFile(attributionSource, uri, "r", null);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a181402e2d9f..b2fdee7a6f89 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -3747,7 +3747,8 @@ class UserController implements Handler.Callback {
synchronized (mUserSwitchingDialogLock) {
dismissUserSwitchingDialog(null);
mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
- switchingFromSystemUserMessage, switchingToSystemUserMessage);
+ switchingFromSystemUserMessage, switchingToSystemUserMessage,
+ getWindowManager());
mUserSwitchingDialog.show(onShown);
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 649305f7ee01..6da6a6ecda52 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -19,7 +19,6 @@ package com.android.server.am;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
import android.content.pm.UserInfo;
@@ -38,6 +37,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
@@ -51,6 +51,7 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.util.ObjectUtils;
import com.android.internal.util.UserIcons;
+import com.android.server.wm.WindowManagerService;
/**
* Dialog to show during the user switch. This dialog shows target user's name and their profile
@@ -70,11 +71,14 @@ class UserSwitchingDialog extends Dialog {
protected final UserInfo mNewUser;
private final String mSwitchingFromSystemUserMessage;
private final String mSwitchingToSystemUserMessage;
+ private final WindowManagerService mWindowManager;
protected final Context mContext;
private final int mTraceCookie;
+ private final boolean mNeedToFreezeScreen;
UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser,
- String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage,
+ WindowManagerService windowManager) {
// TODO(b/278857848): Make full screen user switcher cover top part of the screen as well.
// This problem is seen only on phones, it works fine on tablets.
super(context, R.style.Theme_Material_NoActionBar_Fullscreen);
@@ -84,8 +88,10 @@ class UserSwitchingDialog extends Dialog {
mNewUser = newUser;
mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
- mDisableAnimations = ActivityManager.isLowRamDeviceStatic() || SystemProperties.getBoolean(
+ mDisableAnimations = SystemProperties.getBoolean(
"debug.usercontroller.disable_user_switching_dialog_animations", false);
+ mWindowManager = windowManager;
+ mNeedToFreezeScreen = !mDisableAnimations && !isUserSetupComplete(newUser);
mTraceCookie = UserHandle.MAX_SECONDARY_USER_ID * oldUser.id + newUser.id;
inflateContent();
@@ -167,6 +173,11 @@ class UserSwitchingDialog extends Dialog {
: res.getString(R.string.user_switching_message, mNewUser.name);
}
+ private boolean isUserSetupComplete(UserInfo user) {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, /* default= */ 0, user.id) == 1;
+ }
+
@Override
public void show() {
asyncTraceBegin("", 0);
@@ -176,29 +187,24 @@ class UserSwitchingDialog extends Dialog {
@Override
public void dismiss() {
super.dismiss();
+ stopFreezingScreen();
asyncTraceEnd("", 0);
}
public void show(@NonNull Runnable onShown) {
if (DEBUG) Slog.d(TAG, "show called");
show();
-
- if (mDisableAnimations) {
+ startShowAnimation(() -> {
+ startFreezingScreen();
onShown.run();
- } else {
- startShowAnimation(onShown);
- }
+ });
}
public void dismiss(@Nullable Runnable onDismissed) {
if (DEBUG) Slog.d(TAG, "dismiss called");
-
if (onDismissed == null) {
// no animation needed
dismiss();
- } else if (mDisableAnimations) {
- dismiss();
- onDismissed.run();
} else {
startDismissAnimation(() -> {
dismiss();
@@ -207,7 +213,31 @@ class UserSwitchingDialog extends Dialog {
}
}
+ private void startFreezingScreen() {
+ if (!mNeedToFreezeScreen) {
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "startFreezingScreen");
+ Trace.traceBegin(TRACE_TAG, "startFreezingScreen");
+ mWindowManager.startFreezingScreen(0, 0);
+ Trace.traceEnd(TRACE_TAG);
+ }
+
+ private void stopFreezingScreen() {
+ if (!mNeedToFreezeScreen) {
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "stopFreezingScreen");
+ Trace.traceBegin(TRACE_TAG, "stopFreezingScreen");
+ mWindowManager.stopFreezingScreen();
+ Trace.traceEnd(TRACE_TAG);
+ }
+
private void startShowAnimation(Runnable onAnimationEnd) {
+ if (mDisableAnimations) {
+ onAnimationEnd.run();
+ return;
+ }
asyncTraceBegin("-showAnimation", 1);
startDialogAnimation(new AlphaAnimation(0, 1), () -> {
asyncTraceEnd("-showAnimation", 1);
@@ -222,6 +252,11 @@ class UserSwitchingDialog extends Dialog {
}
private void startDismissAnimation(Runnable onAnimationEnd) {
+ if (mDisableAnimations || mNeedToFreezeScreen) {
+ // animations are disabled or screen is frozen, no need to play an animation
+ onAnimationEnd.run();
+ return;
+ }
asyncTraceBegin("-dismissAnimation", 3);
startDialogAnimation(new AlphaAnimation(1, 0), () -> {
asyncTraceEnd("-dismissAnimation", 3);
@@ -231,6 +266,10 @@ class UserSwitchingDialog extends Dialog {
}
private void startProgressAnimation(Runnable onAnimationEnd) {
+ if (mDisableAnimations) {
+ onAnimationEnd.run();
+ return;
+ }
final ImageView progressCircular = findViewById(R.id.progress_circular);
final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) progressCircular.getDrawable();
avd.registerAnimationCallback(new Animatable2.AnimationCallback() {
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 7cdea8d08c58..9429b4c129f5 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -57,6 +57,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@@ -168,7 +169,7 @@ public class SoundDoseHelper {
@NonNull private final AudioHandler mAudioHandler;
@NonNull private final ISafeHearingVolumeController mVolumeController;
- private final boolean mEnableCsd;
+ private final AtomicBoolean mEnableCsd = new AtomicBoolean(false);
private final Object mCsdStateLock = new Object();
@@ -195,7 +196,7 @@ public class SoundDoseHelper {
private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() {
public void onMomentaryExposure(float currentMel, int deviceId) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
Log.w(TAG, "onMomentaryExposure: csd not supported, ignoring callback");
return;
}
@@ -222,7 +223,7 @@ public class SoundDoseHelper {
}
public void onNewCsdValue(float currentCsd, SoundDoseRecord[] records) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
Log.w(TAG, "onNewCsdValue: csd not supported, ignoring value");
return;
}
@@ -272,8 +273,6 @@ public class SoundDoseHelper {
mContext = context;
- mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default);
- initCsd();
initSafeVolumes();
mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
@@ -285,6 +284,10 @@ public class SoundDoseHelper {
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
R.integer.config_safe_media_volume_index) * 10;
+ mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+ // Csd will be initially disabled until the mcc is read in onConfigureSafeMedia()
+ initCsd();
+
mAlarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
}
@@ -310,7 +313,7 @@ public class SoundDoseHelper {
}
float getOutputRs2UpperBound() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return 0.f;
}
@@ -329,7 +332,7 @@ public class SoundDoseHelper {
}
void setOutputRs2UpperBound(float rs2Value) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -347,7 +350,7 @@ public class SoundDoseHelper {
}
float getCsd() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return -1.f;
}
@@ -366,7 +369,7 @@ public class SoundDoseHelper {
}
void setCsd(float csd) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -400,7 +403,7 @@ public class SoundDoseHelper {
}
void resetCsdTimeouts() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -416,7 +419,7 @@ public class SoundDoseHelper {
}
void forceUseFrameworkMel(boolean useFrameworkMel) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -434,7 +437,7 @@ public class SoundDoseHelper {
}
void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -454,7 +457,7 @@ public class SoundDoseHelper {
}
boolean isCsdEnabled() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return false;
}
@@ -697,8 +700,8 @@ public class SoundDoseHelper {
}
/*package*/ void dump(PrintWriter pw) {
- pw.print(" mEnableCsd="); pw.println(mEnableCsd);
- if (mEnableCsd) {
+ pw.print(" mEnableCsd="); pw.println(mEnableCsd.get());
+ if (mEnableCsd.get()) {
synchronized (mCsdStateLock) {
pw.print(" mCurrentCsd="); pw.println(mCurrentCsd);
}
@@ -719,9 +722,11 @@ public class SoundDoseHelper {
pw.println();
}
- /*package*/void reset() {
+ /*package*/void reset() {
Log.d(TAG, "Reset the sound dose helper");
- mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+
+ mSoundDose.compareAndExchange(/*expectedValue=*/null,
+ AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
synchronized (mCsdStateLock) {
try {
@@ -743,7 +748,7 @@ public class SoundDoseHelper {
private void updateDoseAttenuation(int newIndex, int device, int streamType,
boolean isAbsoluteVolume) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -775,17 +780,19 @@ public class SoundDoseHelper {
}
private void initCsd() {
- if (!mEnableCsd) {
- final ISoundDose soundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback);
- if (soundDose == null) {
- Log.w(TAG, "ISoundDose instance is null.");
- return;
- }
- try {
- soundDose.disableCsd();
- } catch (RemoteException e) {
- Log.e(TAG, "Cannot disable CSD", e);
- }
+ ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "ISoundDose instance is null.");
+ return;
+ }
+
+ try {
+ soundDose.setCsdEnabled(mEnableCsd.get());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot disable CSD", e);
+ }
+
+ if (!mEnableCsd.get()) {
return;
}
@@ -829,7 +836,6 @@ public class SoundDoseHelper {
SystemProperties.getBoolean("audio.safemedia.force", false)
|| mContext.getResources().getBoolean(
com.android.internal.R.bool.config_safe_media_volume_enabled);
-
boolean safeMediaVolumeBypass =
SystemProperties.getBoolean("audio.safemedia.bypass", false);
@@ -860,6 +866,13 @@ public class SoundDoseHelper {
mAudioHandler.obtainMessage(MSG_PERSIST_SAFE_VOLUME_STATE,
persistedState, /*arg2=*/0,
/*obj=*/null), /*delay=*/0);
+
+ boolean newEnableCsd = SystemProperties.getBoolean("audio.safemedia.force", false)
+ || mContext.getResources().getBoolean(
+ R.bool.config_safe_sound_dosage_enabled);
+ if (mEnableCsd.compareAndSet(!newEnableCsd, newEnableCsd)) {
+ initCsd();
+ }
}
}
}
@@ -913,7 +926,7 @@ public class SoundDoseHelper {
// legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP
// instead of computing it from the volume curves
if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
- || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd) {
+ || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd.get()) {
return mSafeMediaVolumeIndex;
}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index fd94be9a2921..17f928af1c09 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -65,7 +65,6 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
@@ -1067,10 +1066,10 @@ public class DisplayModeDirector {
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
- private final Uri mSmoothDisplaySetting =
- Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY);
- private final Uri mForcePeakRefreshRateSetting =
- Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE);
+ private final Uri mPeakRefreshRateSetting =
+ Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+ private final Uri mMinRefreshRateSetting =
+ Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
private final Uri mLowPowerModeSetting =
Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
private final Uri mMatchContentFrameRateSetting =
@@ -1106,8 +1105,9 @@ public class DisplayModeDirector {
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
- mInjector.registerSmoothDisplayObserver(cr, this);
- mInjector.registerForcePeakRefreshRateObserver(cr, this);
+ mInjector.registerPeakRefreshRateObserver(cr, this);
+ cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
+ UserHandle.USER_SYSTEM);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
@@ -1149,8 +1149,8 @@ public class DisplayModeDirector {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
synchronized (mLock) {
- if (mSmoothDisplaySetting.equals(uri)
- || mForcePeakRefreshRateSetting.equals(uri)) {
+ if (mPeakRefreshRateSetting.equals(uri)
+ || mMinRefreshRateSetting.equals(uri)) {
updateRefreshRateSettingLocked();
} else if (mLowPowerModeSetting.equals(uri)) {
updateLowPowerModeSettingLocked();
@@ -1205,9 +1205,12 @@ public class DisplayModeDirector {
}
private void updateRefreshRateSettingLocked() {
- updateRefreshRateSettingLocked(RefreshRateSettingsUtils.getMinRefreshRate(mContext),
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, mDefaultPeakRefreshRate),
- mDefaultRefreshRate);
+ final ContentResolver cr = mContext.getContentResolver();
+ float minRefreshRate = Settings.System.getFloatForUser(cr,
+ Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
+ float peakRefreshRate = Settings.System.getFloatForUser(cr,
+ Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
+ updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
}
private void updateRefreshRateSettingLocked(
@@ -2840,17 +2843,12 @@ public class DisplayModeDirector {
}
interface Injector {
- Uri SMOOTH_DISPLAY_URI = Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY);
- Uri FORCE_PEAK_REFRESH_RATE_URI =
- Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE);
+ Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
@NonNull
DeviceConfigInterface getDeviceConfig();
- void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer);
-
- void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
+ void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
@@ -2890,16 +2888,9 @@ public class DisplayModeDirector {
}
@Override
- public void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- cr.registerContentObserver(SMOOTH_DISPLAY_URI, false /*notifyDescendants*/,
- observer, UserHandle.USER_SYSTEM);
- }
-
- @Override
- public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
+ public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
- cr.registerContentObserver(FORCE_PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
+ cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 220a438d55ee..b82129bdf82f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -35,6 +35,7 @@ public class HdmiCecMessageValidator {
ERROR_DESTINATION,
ERROR_PARAMETER,
ERROR_PARAMETER_SHORT,
+ ERROR_PARAMETER_LONG,
})
public @interface ValidationResult {};
@@ -43,6 +44,7 @@ public class HdmiCecMessageValidator {
static final int ERROR_DESTINATION = 2;
static final int ERROR_PARAMETER = 3;
static final int ERROR_PARAMETER_SHORT = 4;
+ static final int ERROR_PARAMETER_LONG = 5;
interface ParameterValidator {
/**
@@ -159,11 +161,13 @@ public class HdmiCecMessageValidator {
addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
new AsciiValidator(3), DEST_BROADCAST);
- ParameterValidator statusRequestValidator = new OneByteRangeValidator(0x01, 0x03);
+ ParameterValidator statusRequestValidator = new MinimumOneByteRangeValidator(0x01, 0x03);
addValidationInfo(
- Constants.MESSAGE_DECK_CONTROL, new OneByteRangeValidator(0x01, 0x04), DEST_DIRECT);
+ Constants.MESSAGE_DECK_CONTROL,
+ new MinimumOneByteRangeValidator(0x01, 0x04), DEST_DIRECT);
addValidationInfo(
- Constants.MESSAGE_DECK_STATUS, new OneByteRangeValidator(0x11, 0x1F), DEST_DIRECT);
+ Constants.MESSAGE_DECK_STATUS,
+ new MinimumOneByteRangeValidator(0x11, 0x1F), DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_GIVE_DECK_STATUS, statusRequestValidator, DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_PLAY, new PlayModeValidator(), DEST_DIRECT);
@@ -201,9 +205,11 @@ public class HdmiCecMessageValidator {
// Messages for the Device Menu Control.
addValidationInfo(
- Constants.MESSAGE_MENU_REQUEST, new OneByteRangeValidator(0x00, 0x02), DEST_DIRECT);
+ Constants.MESSAGE_MENU_REQUEST,
+ new MinimumOneByteRangeValidator(0x00, 0x02), DEST_DIRECT);
addValidationInfo(
- Constants.MESSAGE_MENU_STATUS, new OneByteRangeValidator(0x00, 0x01), DEST_DIRECT);
+ Constants.MESSAGE_MENU_STATUS,
+ new MinimumOneByteRangeValidator(0x00, 0x01), DEST_DIRECT);
// Messages for the Remote Control Passthrough.
addValidationInfo(
@@ -214,7 +220,7 @@ public class HdmiCecMessageValidator {
// Messages for the Power Status.
addValidationInfo(
Constants.MESSAGE_REPORT_POWER_STATUS,
- new OneByteRangeValidator(0x00, 0x03),
+ new MinimumOneByteRangeValidator(0x00, 0x03),
DEST_DIRECT | DEST_BROADCAST);
// Messages for the General Protocol.
@@ -229,17 +235,17 @@ public class HdmiCecMessageValidator {
oneByteValidator, DEST_DIRECT);
addValidationInfo(
Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
- new OneByteRangeValidator(0x00, 0x01),
+ new MinimumOneByteRangeValidator(0x00, 0x01),
DEST_ALL);
addValidationInfo(
Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
- new OneByteRangeValidator(0x00, 0x01),
+ new SingleByteRangeValidator(0x00, 0x01),
DEST_DIRECT);
// Messages for the Audio Rate Control.
addValidationInfo(
Constants.MESSAGE_SET_AUDIO_RATE,
- new OneByteRangeValidator(0x00, 0x06),
+ new MinimumOneByteRangeValidator(0x00, 0x06),
DEST_DIRECT);
// Messages for Feature Discovery.
@@ -900,11 +906,32 @@ public class HdmiCecMessageValidator {
}
}
- /** Check if the given parameters are one byte parameters and within range. */
- private static class OneByteRangeValidator implements ParameterValidator {
+ /**
+ * Check if the given parameters are at least one byte parameters
+ * and the first byte is within range.
+ */
+ private static class MinimumOneByteRangeValidator implements ParameterValidator {
+ private final int mMinValue, mMaxValue;
+
+ MinimumOneByteRangeValidator(int minValue, int maxValue) {
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ }
+
+ @Override
+ public int isValid(byte[] params) {
+ if (params.length < 1) {
+ return ERROR_PARAMETER_SHORT;
+ }
+ return toErrorCode(isWithinRange(params[0], mMinValue, mMaxValue));
+ }
+ }
+
+ /** Check if the given parameters are exactly one byte parameters and within range. */
+ private static class SingleByteRangeValidator implements ParameterValidator {
private final int mMinValue, mMaxValue;
- OneByteRangeValidator(int minValue, int maxValue) {
+ SingleByteRangeValidator(int minValue, int maxValue) {
mMinValue = minValue;
mMaxValue = maxValue;
}
@@ -913,6 +940,8 @@ public class HdmiCecMessageValidator {
public int isValid(byte[] params) {
if (params.length < 1) {
return ERROR_PARAMETER_SHORT;
+ } else if (params.length > 1) {
+ return ERROR_PARAMETER_LONG;
}
return toErrorCode(isWithinRange(params[0], mMinValue, mMaxValue));
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 75fe63a66206..9cd5272b356b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1611,6 +1611,7 @@ public class HdmiControlService extends SystemService {
@HdmiCecMessageValidator.ValidationResult
int validationResult = message.getValidationResult();
if (validationResult == HdmiCecMessageValidator.ERROR_PARAMETER
+ || validationResult == HdmiCecMessageValidator.ERROR_PARAMETER_LONG
|| !verifyPhysicalAddresses(message)) {
return Constants.ABORT_INVALID_OPERAND;
} else if (validationResult != HdmiCecMessageValidator.OK
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 92f0011339a5..9c3b38ab51a3 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -395,7 +395,7 @@ public final class SingleKeyGestureDetector {
private class KeyHandler extends Handler {
KeyHandler() {
- super(Looper.getMainLooper());
+ super(Looper.myLooper());
}
@Override
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 98d2d3d4b997..9020cb3405a2 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1353,9 +1353,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
void complete() {
// Only changes from home+lock to just home or lock need attention
- // If setting the wallpaper fails, this callback will be called
- // when the wallpaper is detached, in which case wallpapers may have
- // already changed. Make sure we're not overwriting a more recent wallpaper.
if (mNewWallpaper.mSystemWasBoth) {
if (DEBUG) {
Slog.v(TAG, "Handling change from system+lock wallpaper");
@@ -1378,7 +1375,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mOriginalSystem.wallpaperComponent;
lockWp.connection = mOriginalSystem.connection;
lockWp.connection.mWallpaper = lockWp;
- updateEngineFlags(mOriginalSystem, FLAG_LOCK);
+ mOriginalSystem.mWhich = FLAG_LOCK;
+ updateEngineFlags(mOriginalSystem);
notifyWallpaperColorsChanged(lockWp, FLAG_LOCK);
} else {
// Failed rename, use current system wp for both
@@ -1387,7 +1385,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
currentSystem.mWhich = FLAG_SYSTEM | FLAG_LOCK;
- updateEngineFlags(currentSystem, FLAG_SYSTEM | FLAG_LOCK);
+ updateEngineFlags(currentSystem);
mLockWallpaperMap.remove(mNewWallpaper.userId);
}
} else {
@@ -1396,7 +1394,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.v(TAG, "live system+lock to system success");
}
mOriginalSystem.mWhich = FLAG_LOCK;
- updateEngineFlags(mOriginalSystem, FLAG_LOCK);
+ updateEngineFlags(mOriginalSystem);
mLockWallpaperMap.put(mNewWallpaper.userId, mOriginalSystem);
mLastLockWallpaper = mOriginalSystem;
notifyWallpaperColorsChanged(mOriginalSystem, FLAG_LOCK);
@@ -1409,7 +1407,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
if (currentSystem.wallpaperId == mOriginalSystem.wallpaperId) {
currentSystem.mWhich = FLAG_SYSTEM;
- updateEngineFlags(currentSystem, FLAG_SYSTEM);
+ updateEngineFlags(currentSystem);
}
}
}
@@ -1422,24 +1420,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.v(TAG, "new lastLockWp: " + mLastLockWallpaper);
}
}
-
- private void updateEngineFlags(WallpaperData wallpaper, @SetWallpaperFlags int which) {
- if (wallpaper.connection == null) {
- return;
- }
- wallpaper.connection.forEachDisplayConnector(
- connector -> {
- try {
- if (connector.mEngine != null) {
- connector.mEngine.setWallpaperFlags(which);
- mWindowManagerInternal.setWallpaperShowWhenLocked(
- connector.mToken, (which & FLAG_LOCK) != 0);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to update wallpaper engine flags", e);
- }
- });
- }
}
class MyPackageMonitor extends PackageMonitor {
@@ -3095,6 +3075,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
newWallpaper.userId);
if (lockedWallpaper != null) {
detachWallpaperLocked(lockedWallpaper);
+ if (same) {
+ updateEngineFlags(newWallpaper);
+ }
}
mLockWallpaperMap.remove(newWallpaper.userId);
}
@@ -3430,6 +3413,27 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ // Updates the given wallpaper's Engine so that its destination flags are the same as those of
+ // the wallpaper, e.g., after a wallpaper has been changed from displaying on home+lock to home
+ // or lock only.
+ private void updateEngineFlags(WallpaperData wallpaper) {
+ if (wallpaper.connection == null) {
+ return;
+ }
+ wallpaper.connection.forEachDisplayConnector(
+ connector -> {
+ try {
+ if (connector.mEngine != null) {
+ connector.mEngine.setWallpaperFlags(wallpaper.mWhich);
+ mWindowManagerInternal.setWallpaperShowWhenLocked(
+ connector.mToken, (wallpaper.mWhich & FLAG_LOCK) != 0);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to update wallpaper engine flags", e);
+ }
+ });
+ }
+
private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
wallpaper.wallpaperComponent = null;
detachWallpaperLocked(wallpaper);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 78c066bdc212..c3cd3eca84f2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5400,7 +5400,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
if (inFinishingTransition) {
- // Let the finishing transition commit the visibility.
+ // Let the finishing transition commit the visibility, but let the controller know
+ // about it so that we can recover from degenerate cases.
+ mTransitionController.mValidateCommitVis.add(this);
return;
}
// If we are preparing an app transition, then delay changing
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f5079d37b324..e47787e97f20 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -33,6 +33,7 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Process.SYSTEM_UID;
+import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
@@ -209,7 +210,8 @@ class RecentTasks {
private final PointerEventListener mListener = new PointerEventListener() {
@Override
public void onPointerEvent(MotionEvent ev) {
- if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN) {
+ if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN
+ || ev.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE) {
// Skip if we aren't freezing or starting a gesture
return;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3f4296a5eee5..d3edeaebbf99 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3239,7 +3239,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId)
!= null) {
mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
- task.getTaskInfo());
+ task.getTaskInfo(), userId);
}
}, true /* traverseTopToBottom */);
}
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 49d064f83345..9324e29daafb 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -144,7 +144,7 @@ class TaskChangeNotificationController {
};
private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> {
- l.onTaskProfileLocked((RunningTaskInfo) m.obj);
+ l.onTaskProfileLocked((RunningTaskInfo) m.obj, m.arg1);
};
private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> {
@@ -467,9 +467,9 @@ class TaskChangeNotificationController {
* Notify listeners that the task has been put in a locked state because one or more of the
* activities inside it belong to a managed profile user that has been locked.
*/
- void notifyTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo) {
+ void notifyTaskProfileLocked(RunningTaskInfo taskInfo, int userId) {
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG,
- taskInfo);
+ userId, 0, taskInfo);
forAllLocalListeners(mNotifyTaskProfileLocked, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index d531ad175f10..0f74aebc2fde 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -55,6 +55,7 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TASK_LAUNCHING_BEHIND;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
@@ -65,12 +66,14 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -85,6 +88,7 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.ScreenCapture;
import android.window.TransitionInfo;
+import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -185,7 +189,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
/** The final animation targets derived from participants after promotion. */
- private ArrayList<ChangeInfo> mTargets;
+ ArrayList<ChangeInfo> mTargets;
/** The displays that this transition is running on. */
private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
@@ -271,9 +275,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
/** Any 2 transitions of this type can run in parallel with each other. Used for testing. */
static final int PARALLEL_TYPE_MUTUAL = 1;
+ /** This is a recents transition. */
+ static final int PARALLEL_TYPE_RECENTS = 2;
+
+
@IntDef(prefix = { "PARALLEL_TYPE_" }, value = {
PARALLEL_TYPE_NONE,
- PARALLEL_TYPE_MUTUAL
+ PARALLEL_TYPE_MUTUAL,
+ PARALLEL_TYPE_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
@interface ParallelType {}
@@ -328,6 +337,21 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mFlags |= flag;
}
+ void calcParallelCollectType(WindowContainerTransaction wct) {
+ for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp hop = wct.getHierarchyOps().get(i);
+ if (hop.getType() != HIERARCHY_OP_TYPE_PENDING_INTENT) continue;
+ final Bundle b = hop.getLaunchOptions();
+ if (b == null || b.isEmpty()) continue;
+ final boolean transientLaunch = b.getBoolean(ActivityOptions.KEY_TRANSIENT_LAUNCH);
+ if (transientLaunch) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Starting a Recents transition which can be parallel.");
+ mParallelCollectType = PARALLEL_TYPE_RECENTS;
+ }
+ }
+ }
+
/** Records an activity as transient-launch. This activity must be already collected. */
void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelow) {
if (mTransientLaunches == null) {
@@ -380,6 +404,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return false;
}
+ boolean hasTransientLaunch() {
+ return mTransientLaunches != null && !mTransientLaunches.isEmpty();
+ }
+
boolean isTransientLaunch(@NonNull ActivityRecord activity) {
return mTransientLaunches != null && mTransientLaunches.containsKey(activity);
}
@@ -984,13 +1012,20 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// Record all the now-hiding activities so that they are committed. Just use
// mParticipants because we can avoid a new list this way.
for (int i = 0; i < mTransientHideTasks.size(); ++i) {
- // Only worry about tasks that were actually hidden. Otherwise, we could end-up
- // committing visibility for activity-level changes that aren't part of this
- // transition.
- if (mTransientHideTasks.get(i).isVisibleRequested()) continue;
- mTransientHideTasks.get(i).forAllActivities(r -> {
+ final Task rootTask = mTransientHideTasks.get(i);
+ rootTask.forAllActivities(r -> {
// Only check leaf-tasks that were collected
if (!mParticipants.contains(r.getTask())) return;
+ if (rootTask.isVisibleRequested()) {
+ // This transient-hide didn't hide, so don't commit anything (otherwise we
+ // could prematurely commit invisible on unrelated activities). To be safe,
+ // though, notify the controller to prevent degenerate cases.
+ if (!r.isVisibleRequested()) {
+ mController.mValidateCommitVis.add(r);
+ }
+ return;
+ }
+ // This did hide: commit immediately so that other transitions know about it.
mParticipants.add(r);
});
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 7950edaaa267..e1da91a6cb21 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -133,6 +133,13 @@ class TransitionController {
final ArrayList<Runnable> mStateValidators = new ArrayList<>();
/**
+ * List of activity-records whose visibility changed outside the main/tracked part of a
+ * transition (eg. in the finish-transaction). These will be checked when idle to recover from
+ * degenerate states.
+ */
+ final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>();
+
+ /**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
@@ -848,6 +855,15 @@ class TransitionController {
}
}
mStateValidators.clear();
+ for (int i = 0; i < mValidateCommitVis.size(); ++i) {
+ final ActivityRecord ar = mValidateCommitVis.get(i);
+ if (!ar.isVisibleRequested() && ar.isVisible()) {
+ Slog.e(TAG, "Uncommitted visibility change: " + ar);
+ ar.commitVisibility(ar.isVisibleRequested(), false /* layout */,
+ false /* fromTransition */);
+ }
+ }
+ mValidateCommitVis.clear();
}
/**
@@ -879,6 +895,8 @@ class TransitionController {
// If it's a legacy sync, then it needs to wait until there is no collecting transition.
if (queued.mTransition == null) return;
if (!canStartCollectingNow(queued.mTransition)) return;
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from collecting"
+ + " to waiting.", mCollectingTransition.getSyncId());
mWaitingTransitions.add(mCollectingTransition);
mCollectingTransition = null;
} else if (mSyncEngine.hasActiveSync()) {
@@ -930,10 +948,37 @@ class TransitionController {
* in {@link #getIsIndependent} later.
*/
boolean getCanBeIndependent(Transition collecting, Transition queued) {
+ // For tests
if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL
&& collecting.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) {
return true;
}
+ // For recents
+ if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ // Must serialize with itself.
+ return false;
+ }
+ // allow this if `collecting` only has activities
+ for (int i = 0; i < collecting.mParticipants.size(); ++i) {
+ final WindowContainer wc = collecting.mParticipants.valueAt(i);
+ final ActivityRecord ar = wc.asActivityRecord();
+ if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) {
+ // Is task or above, so can't be independent
+ return false;
+ }
+ if (ar != null && ar.isActivityTypeHomeOrRecents()) {
+ // It's a recents or home type, so it conflicts.
+ return false;
+ }
+ }
+ return true;
+ } else if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ // We can collect simultaneously with recents if it is populated. This is because
+ // we know that recents will not collect/trampoline any more stuff. If anything in the
+ // queued transition overlaps, it will end up just waiting in sync-queue anyways.
+ return true;
+ }
return false;
}
@@ -942,11 +987,47 @@ class TransitionController {
* `running` is playing based on its current state.
*/
static boolean getIsIndependent(Transition running, Transition incoming) {
+ // For tests
if (running.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL
&& incoming.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) {
return true;
}
- return false;
+ // For now there's only one mutually-independent pair: an all activity-level transition and
+ // a transient-launch where none of the activities are part of the transient-launch task,
+ // so the following logic is hard-coded specifically for this.
+ // Also, we currently restrict valid transient-launches to just recents.
+ final Transition recents;
+ final Transition other;
+ if (running.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS
+ && running.hasTransientLaunch()) {
+ if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ // Recents can't be independent from itself.
+ return false;
+ }
+ recents = running;
+ other = incoming;
+ } else if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS
+ && incoming.hasTransientLaunch()) {
+ recents = incoming;
+ other = running;
+ } else {
+ return false;
+ }
+ // Check against *targets* because that is the post-promotion set of containers that are
+ // actually animating.
+ for (int i = 0; i < other.mTargets.size(); ++i) {
+ final WindowContainer wc = other.mTargets.get(i).mContainer;
+ final ActivityRecord ar = wc.asActivityRecord();
+ if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) {
+ // Is task or above, so for now don't let them be independent.
+ return false;
+ }
+ if (ar != null && recents.isTransientLaunch(ar)) {
+ // Change overlaps with recents, so serialize.
+ return false;
+ }
+ }
+ return true;
}
void assignTrack(Transition transition, TransitionInfo info) {
@@ -970,9 +1051,15 @@ class TransitionController {
if (track < 0) {
// Didn't overlap with anything, so give it its own track
track = mTrackCount;
+ if (track > 0) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Playing #%d in parallel on "
+ + "track #%d", transition.getSyncId(), track);
+ }
}
if (sync) {
info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Marking #%d animation as SYNC.",
+ transition.getSyncId());
}
transition.mAnimationTrack = track;
info.setTrack(track);
@@ -1145,6 +1232,8 @@ class TransitionController {
// Check if we can run in parallel here.
if (canStartCollectingNow(transit)) {
// start running in parallel.
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from"
+ + " collecting to waiting.", mCollectingTransition.getSyncId());
mWaitingTransitions.add(mCollectingTransition);
mCollectingTransition = null;
moveToCollecting(transit);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index cd42528ad79b..d5aa520e1b6e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -299,6 +299,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final boolean needsSetReady = t != null;
final Transition nextTransition = new Transition(type, 0 /* flags */,
mTransitionController, mService.mWindowManager.mSyncEngine);
+ nextTransition.calcParallelCollectType(wct);
mTransitionController.startCollectOrQueue(nextTransition,
(deferred) -> {
nextTransition.start();
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index fc7fd1afe58f..b073ff400e44 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -98,7 +98,9 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
mRequestId, mClientRequest,
mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
- Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
+ Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
+ // TODO(b/279480457): populate
+ /*defaultProviderId=*/new ArrayList<>()),
providerDataList);
mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 50e5163cea55..4e82ee71ac1b 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -35,6 +35,7 @@ import java.util.Map;
/**
* For all future metric additions, this will contain their names for local usage after importing
* from {@link com.android.internal.util.FrameworkStatsLog}.
+ * TODO(b/271135048) - Emit all atoms, including all V4 atoms (specifically the rest of track 1).
*/
public class MetricUtilities {
private static final boolean LOG_FLAG = true;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index a62d9e805a93..0c3d2a4c000a 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -40,7 +40,6 @@ import android.service.credentials.CredentialEntry;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.GetCredentialRequest;
import android.service.credentials.RemoteEntry;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -413,11 +412,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
*/
private boolean onAuthenticationEntrySelected(
@Nullable ProviderPendingIntentResponse providerPendingIntentResponse) {
- Log.i(TAG, "onAuthenticationEntrySelected");
// Authentication entry is expected to have a BeginGetCredentialResponse instance. If it
// does not have it, we remove the authentication entry and do not add any more content.
if (providerPendingIntentResponse == null) {
- Log.i(TAG, "providerPendingIntentResponse is null");
// Nothing received. This is equivalent to no content received.
return false;
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index c10f5640c466..ead86cefc5d4 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -266,7 +266,7 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption
.collect(Collectors.toList());
updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED,
/*source=*/ CredentialsSource.REGISTRY);
- // TODO(use metric later)
+ // TODO(b/273353677) : metric should be emitted similarly to sibling classes
}
@Nullable
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index d02a8c1ee510..73fdc1ce2635 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -268,12 +268,9 @@ public abstract class ProviderSession<T, R>
/*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) {
return true;
}
- } catch (SecurityException e) {
+ } catch (SecurityException | PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Error getting info for " + mComponentName.flattenToString(), e);
return false;
- } catch (PackageManager.NameNotFoundException e) {
- Slog.i(TAG, "Error getting info for " + mComponentName.flattenToString(), e);
- return false;
}
return false;
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
index 721d3d782653..b212606b8271 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
@@ -30,7 +30,6 @@ import java.util.stream.Collectors;
* Some types are redundant across these metric collectors, but that has debug use-cases as
* these data-types are available at different moments of the flow (and typically, one can feed
* into the next).
- * TODO(b/270403549) - iterate on this in V3+
*/
public class CandidatePhaseMetric {
@@ -56,10 +55,7 @@ public class CandidatePhaseMetric {
private int mProviderQueryStatus = -1;
// Indicates if an exception was thrown by this provider, false by default
private boolean mHasException = false;
- // Indicates the number of total entries available. We can also locally store the entries, but
- // cannot emit them in the current split form. TODO(b/271135048) - possibly readjust candidate
- // entries. Also, it may be okay to remove this and instead aggregate from inner counts.
- // Defaults to -1
+ // Indicates the number of total entries available, defaults to -1
private int mNumEntriesTotal = -1;
// The count of action entries from this provider, defaults to -1
private int mActionEntryCount = -1;
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
index c80cc24fa455..8f08bb02dfd0 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
@@ -29,11 +29,8 @@ import java.util.List;
* Some types are redundant across these metric collectors, but that has debug use-cases as
* these data-types are available at different moments of the flow (and typically, one can feed
* into the next).
- * TODO(b/270403549) - iterate on this in V3+
*/
public class ChosenProviderFinalPhaseMetric {
-
- // TODO(b/270403549) - applies elsewhere, likely removed or replaced w/ some hashed/count index
private static final String TAG = "ChosenFinalPhaseMetric";
// The session id associated with this API call, used to unite split emits
private int mSessionId = -1;
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 0ecd9cc79e48..5cfb0e7d375b 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -26,7 +26,6 @@ import java.util.Map;
* Some types are redundant across these metric collectors, but that has debug use-cases as
* these data-types are available at different moments of the flow (and typically, one can feed
* into the next).
- * TODO(b/270403549) - iterate on this in V3+
*/
public class InitialPhaseMetric {
private static final String TAG = "InitialPhaseMetric";
@@ -47,7 +46,6 @@ public class InitialPhaseMetric {
private long mCredentialServiceBeginQueryTimeNanoseconds = -1;
// Indicates if the origin was specified when making this API request
- // TODO(b/271135048) - Emit once metrics approved
private boolean mOriginSpecified = false;
// Stores the deduped request information, particularly {"req":5}.
diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
index 547c09a625f6..4ecdfef401b6 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
@@ -337,10 +337,6 @@ public class RequestSessionMetric {
*/
public void logApiCalledAtFinish(int apiStatus) {
try {
- // TODO (b/270403549) - this browsing phase object is fine but also have a new emit
- // For the returned types by authentication entries - i.e. a CandidatePhase During
- // Browse
- // Possibly think of adding in more atoms for other APIs as well.
logApiCalledFinalPhase(mChosenProviderFinalPhaseMetric, mCandidateBrowsingPhaseMetric,
apiStatus,
++mSequenceCounter);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 231fee358a2f..675ebd3ddd60 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -39,6 +39,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_DISPLAY;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_FUN;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_KEYGUARD;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCALE;
@@ -873,7 +874,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// TODO(b/265683382) remove the flag after rollout.
private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
- private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
+ private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = true;
private static final String ENABLE_WORK_PROFILE_TELEPHONY_FLAG =
"enable_work_profile_telephony";
@@ -12195,7 +12196,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
+ if (isPolicyEngineForFinanceFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
@@ -12205,7 +12206,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int userId = getProfileParentUserIfRequested(
caller.getUserId(), calledOnParentInstance);
if (calledOnParentInstance) {
- if (!isPermissionCheckFlagEnabled()) {
+ if (!isPolicyEngineForFinanceFlagEnabled()) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller));
}
@@ -12213,7 +12214,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
"Permitted input methods must allow all input methods or only "
+ "system input methods when called on the parent instance of an "
+ "organization-owned device");
- } else if (!isPermissionCheckFlagEnabled()) {
+ } else if (!isPolicyEngineForFinanceFlagEnabled()) {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
@@ -12241,7 +12242,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (getLockObject()) {
if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName);
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+ who, MANAGE_DEVICE_POLICY_INPUT_METHODS,
+ caller.getPackageName(), userId);
mDevicePolicyEngine.setLocalPolicy(
PolicyDefinition.PERMITTED_INPUT_METHODS,
admin,
@@ -13436,6 +13439,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public void setUserRestrictionGlobally(String callerPackage, String key) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
+ EnforcingAdmin admin = enforcePermissionForUserRestriction(
+ /* who= */ null,
+ key,
+ caller.getPackageName(),
+ UserHandle.USER_ALL
+ );
+
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
if (!isPolicyEngineForFinanceFlagEnabled()) {
@@ -13452,13 +13462,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
throw new IllegalArgumentException("Invalid restriction key: " + key);
}
- EnforcingAdmin admin = enforcePermissionForUserRestriction(
- /* who= */ null,
- key,
- caller.getPackageName(),
- UserHandle.USER_ALL
- );
-
setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true);
logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
@@ -16371,7 +16374,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
// the admin for the calling user.
Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple "
- + "sources for restriction %s on user %d", restriction, userId);
+ + "sources for restriction %s on user %d",
+ userId, restriction, restriction, userId);
result = new Bundle();
result.putInt(Intent.EXTRA_USER_ID, userId);
return result;
@@ -22839,6 +22843,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FACTORY_RESET,
MANAGE_DEVICE_POLICY_FUN,
+ MANAGE_DEVICE_POLICY_INPUT_METHODS,
MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_LOCALE,
@@ -22914,9 +22919,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_CALLS,
MANAGE_DEVICE_POLICY_CAMERA,
+ MANAGE_DEVICE_POLICY_CERTIFICATES,
MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FACTORY_RESET,
+ MANAGE_DEVICE_POLICY_INPUT_METHODS,
MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_LOCALE,
@@ -22949,7 +22956,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS,
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_CERTIFICATES,
MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
MANAGE_DEVICE_POLICY_DEFAULT_SMS,
MANAGE_DEVICE_POLICY_LOCALE,
@@ -23074,11 +23080,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
//Map of Permission to Delegate Scope.
private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>();
{
- DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, DELEGATION_APP_RESTRICTIONS);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL, DELEGATION_BLOCK_UNINSTALL);
- DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING);
+ DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_CERTIFICATES, DELEGATION_CERT_INSTALL);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, DELEGATION_PACKAGE_ACCESS);
+ DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT);
+ DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING);
}
private static final HashMap<String, String> CROSS_USER_PERMISSIONS =
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 3a47b476a131..2b57c5927678 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -154,6 +154,7 @@ public class DeviceIdleControllerTest {
// Freeze time for testing.
long nowElapsed;
boolean useMotionSensor = true;
+ boolean isLocationPrefetchEnabled = true;
InjectorForTest(Context ctx) {
super(ctx);
@@ -223,6 +224,11 @@ public class DeviceIdleControllerTest {
}
@Override
+ boolean isLocationPrefetchEnabled() {
+ return isLocationPrefetchEnabled;
+ }
+
+ @Override
PowerManager getPowerManager() {
return mPowerManager;
}
@@ -991,6 +997,43 @@ public class DeviceIdleControllerTest {
}
@Test
+ public void testStepIdleStateLocked_ValidStates_LocationPrefetchDisabled() {
+ mInjector.locationManager = mLocationManager;
+ mInjector.isLocationPrefetchEnabled = false;
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+ doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ setAlarmSoon(false);
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Prefetch location is off, so SENSING should go straight through to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() {
// Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
setAlarmSoon(false);
@@ -1024,6 +1067,46 @@ public class DeviceIdleControllerTest {
}
@Test
+ public void testStepIdleStateLocked_ValidStates_WithLocationManager_MissingProviders() {
+ mInjector.locationManager = mLocationManager;
+ doReturn(null).when(mLocationManager)
+ .getProvider(eq(LocationManager.FUSED_PROVIDER));
+ doReturn(null).when(mLocationManager)
+ .getProvider(eq(LocationManager.GPS_PROVIDER));
+ doReturn(mock(LocationProvider.class)).when(mLocationManager)
+ .getProvider(eq(LocationManager.NETWORK_PROVIDER));
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ setAlarmSoon(false);
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Location manager exists, but the required providers don't exist,
+ // so SENSING should go straight through to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() {
mInjector.locationManager = mLocationManager;
doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
deleted file mode 100644
index 17fba9f43707..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import android.hardware.display.DisplayManager;
-import android.provider.Settings;
-import android.testing.TestableContext;
-import android.view.Display;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.display.RefreshRateSettingsUtils;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RefreshRateSettingsUtilsTest {
-
- @Rule
- public final TestableContext mContext = new TestableContext(
- InstrumentationRegistry.getInstrumentation().getContext());
-
- @Mock
- private DisplayManager mDisplayManagerMock;
- @Mock
- private Display mDisplayMock;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock);
-
- Display.Mode[] modes = new Display.Mode[] {
- new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
- /* refreshRate= */ 60),
- new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
- /* refreshRate= */ 120),
- new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
- /* refreshRate= */ 90)
- };
-
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
- when(mDisplayMock.getSupportedModes()).thenReturn(modes);
- }
-
- @Test
- public void testFindHighestRefreshRateForDefaultDisplay() {
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
- assertEquals(DEFAULT_REFRESH_RATE,
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* delta= */ 0);
-
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
- assertEquals(120,
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* delta= */ 0);
- }
-
- @Test
- public void testGetMinRefreshRate() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1);
- assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, 0);
- assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, 1);
- assertEquals(120, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
- }
-
- @Test
- public void testGetPeakRefreshRate() {
- float defaultPeakRefreshRate = 100;
-
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1);
- assertEquals(defaultPeakRefreshRate,
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
- /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 0);
- assertEquals(DEFAULT_REFRESH_RATE,
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
- /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 1);
- assertEquals(120,
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
- /* delta= */ 0);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java
new file mode 100644
index 000000000000..eec026ccfc8a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.companion.transport.CompanionTransportManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CrossDeviceSyncControllerTest {
+
+ private CrossDeviceSyncController mCrossDeviceSyncController;
+ @Mock
+ private CompanionTransportManager mMockCompanionTransportManager;
+ @Mock
+ private CrossDeviceCall mMockCrossDeviceCall;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mCrossDeviceSyncController = new CrossDeviceSyncController(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ mMockCompanionTransportManager);
+ }
+
+ @Test
+ public void processTelecomDataFromSync_createCallUpdateMessage_emptyCallsAndRequests() {
+ final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage(new HashSet<>(),
+ InstrumentationRegistry.getInstrumentation().getContext().getUserId());
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Unexpectedly found a call").that(
+ callMetadataSyncData.getCalls()).isEmpty();
+ assertWithMessage("Unexpectedly found a request").that(
+ callMetadataSyncData.getRequests()).isEmpty();
+ }
+
+ @Test
+ public void processTelecomDataFromSync_createEmptyMessage_emptyCallsAndRequests() {
+ final byte[] data = CrossDeviceSyncController.createEmptyMessage();
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Unexpectedly found a call").that(
+ callMetadataSyncData.getCalls()).isEmpty();
+ assertWithMessage("Unexpectedly found a request").that(
+ callMetadataSyncData.getRequests()).isEmpty();
+ }
+
+ @Test
+ public void processTelecomDataFromSync_createCallUpdateMessage_hasCalls() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+ final String callerId = "Firstname Lastname";
+ when(mMockCrossDeviceCall.getReadableCallerId(anyBoolean())).thenReturn(callerId);
+ final String appName = "AppName";
+ when(mMockCrossDeviceCall.getCallingAppName()).thenReturn(appName);
+ final String appIcon = "ABCD";
+ when(mMockCrossDeviceCall.getCallingAppIcon()).thenReturn(appIcon.getBytes());
+ when(mMockCrossDeviceCall.getStatus()).thenReturn(android.companion.Telecom.Call.RINGING);
+ final Set<Integer> controls = Set.of(
+ android.companion.Telecom.Call.ACCEPT,
+ android.companion.Telecom.Call.REJECT,
+ android.companion.Telecom.Call.SILENCE);
+ when(mMockCrossDeviceCall.getControls()).thenReturn(controls);
+ final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage(
+ new HashSet<>(List.of(mMockCrossDeviceCall)),
+ InstrumentationRegistry.getInstrumentation().getContext().getUserId());
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Wrong number of active calls").that(
+ callMetadataSyncData.getCalls()).hasSize(1);
+ final CallMetadataSyncData.Call call =
+ callMetadataSyncData.getCalls().stream().findAny().orElseThrow();
+ assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L);
+ assertWithMessage("Wrong app icon").that(new String(call.getAppIcon())).isEqualTo(appIcon);
+ assertWithMessage("Wrong app name").that(call.getAppName()).isEqualTo(appName);
+ assertWithMessage("Wrong caller id").that(call.getCallerId()).isEqualTo(callerId);
+ assertWithMessage("Wrong status").that(call.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.RINGING);
+ assertWithMessage("Wrong controls").that(call.getControls()).isEqualTo(controls);
+ assertWithMessage("Unexpectedly has requests").that(
+ callMetadataSyncData.getRequests()).isEmpty();
+ }
+
+ @Test
+ public void processTelecomDataFromMessage_createCallControlMessage_hasCallControlRequest() {
+ final byte[] data = CrossDeviceSyncController.createCallControlMessage(
+ /* callId= */ 5L, /* status= */ android.companion.Telecom.Call.ACCEPT);
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Wrong number of requests").that(
+ callMetadataSyncData.getRequests()).hasSize(1);
+ final CallMetadataSyncData.Call call =
+ callMetadataSyncData.getRequests().stream().findAny().orElseThrow();
+ assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L);
+ assertWithMessage("Wrong app icon").that(call.getAppIcon()).isNull();
+ assertWithMessage("Wrong app name").that(call.getAppName()).isNull();
+ assertWithMessage("Wrong caller id").that(call.getCallerId()).isNull();
+ assertWithMessage("Wrong status").that(call.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS);
+ assertWithMessage("Wrong controls").that(call.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT));
+ assertWithMessage("Unexpectedly has active calls").that(
+ callMetadataSyncData.getCalls()).isEmpty();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index e49225216b6b..b2a3a57cccf8 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -84,7 +84,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -159,9 +158,6 @@ public class DisplayModeDirectorTest {
LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
-
- clearSmoothDisplaySetting();
- clearForcePeakRefreshRateSetting();
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
@@ -922,6 +918,7 @@ public class DisplayModeDirectorTest {
public void testLockFpsForLowZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -929,7 +926,6 @@ public class DisplayModeDirectorTest {
config.setRefreshRateInLowZone(90);
config.setLowDisplayBrightnessThresholds(new int[] { 10 });
config.setLowAmbientBrightnessThresholds(new int[] { 20 });
- config.setDefaultPeakRefreshRate(90);
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -980,6 +976,7 @@ public class DisplayModeDirectorTest {
public void testLockFpsForHighZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -987,7 +984,6 @@ public class DisplayModeDirectorTest {
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
- config.setDefaultPeakRefreshRate(90);
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -1035,123 +1031,16 @@ public class DisplayModeDirectorTest {
}
@Test
- public void testSmoothDisplay() {
- float defaultRefreshRate = 60;
- int defaultPeakRefreshRate = 100;
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- final FakeDeviceConfig config = mInjector.getDeviceConfig();
- config.setDefaultPeakRefreshRate(defaultPeakRefreshRate);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- // Default value of the setting
-
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultPeakRefreshRate);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
-
- setSmoothDisplayEnabled(false);
-
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
-
- setSmoothDisplayEnabled(true);
-
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
- }
-
- @Test
- public void testForcePeakRefreshRate() {
- float defaultRefreshRate = 60;
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setForcePeakRefreshRateEnabled(false);
- setSmoothDisplayEnabled(false);
-
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
-
- setForcePeakRefreshRateEnabled(true);
-
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
- }
-
- @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
- config.setDefaultPeakRefreshRate(90);
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -2527,10 +2416,10 @@ public class DisplayModeDirectorTest {
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
- config.setDefaultPeakRefreshRate(90);
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -2828,30 +2717,10 @@ public class DisplayModeDirectorTest {
listener.onDisplayChanged(DISPLAY_ID);
}
- private void setSmoothDisplayEnabled(boolean enabled) {
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY,
- enabled ? 1 : 0);
- mInjector.notifySmoothDisplaySettingChanged();
- waitForIdleSync();
- }
-
- private void clearSmoothDisplaySetting() {
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1);
- mInjector.notifySmoothDisplaySettingChanged();
- waitForIdleSync();
- }
-
- private void setForcePeakRefreshRateEnabled(boolean enabled) {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, enabled ? 1 : 0);
- mInjector.notifyForcePeakRefreshRateSettingChanged();
- waitForIdleSync();
- }
-
- private void clearForcePeakRefreshRateSetting() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1);
- mInjector.notifySmoothDisplaySettingChanged();
+ private void setPeakRefreshRate(float fps) {
+ Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+ fps);
+ mInjector.notifyPeakRefreshRateChanged();
waitForIdleSync();
}
@@ -2900,8 +2769,7 @@ public class DisplayModeDirectorTest {
private final Display mDisplay;
private boolean mDisplayInfoValid = true;
private ContentObserver mBrightnessObserver;
- private ContentObserver mSmoothDisplaySettingObserver;
- private ContentObserver mForcePeakRefreshRateSettingObserver;
+ private ContentObserver mPeakRefreshRateObserver;
FakesInjector() {
mDeviceConfig = new FakeDeviceConfig();
@@ -2918,15 +2786,9 @@ public class DisplayModeDirectorTest {
}
@Override
- public void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- mSmoothDisplaySettingObserver = observer;
- }
-
- @Override
- public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
+ public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
- mForcePeakRefreshRateSettingObserver = observer;
+ mPeakRefreshRateObserver = observer;
}
@Override
@@ -2976,17 +2838,10 @@ public class DisplayModeDirectorTest {
ApplicationProvider.getApplicationContext().getResources());
}
- void notifySmoothDisplaySettingChanged() {
- if (mSmoothDisplaySettingObserver != null) {
- mSmoothDisplaySettingObserver.dispatchChange(false /*selfChange*/,
- SMOOTH_DISPLAY_URI);
- }
- }
-
- void notifyForcePeakRefreshRateSettingChanged() {
- if (mForcePeakRefreshRateSettingObserver != null) {
- mForcePeakRefreshRateSettingObserver.dispatchChange(false /*selfChange*/,
- FORCE_PEAK_REFRESH_RATE_URI);
+ void notifyPeakRefreshRateChanged() {
+ if (mPeakRefreshRateObserver != null) {
+ mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
+ PEAK_REFRESH_RATE_URI);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index a446e109c921..c53a7a708cfd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -18,6 +18,7 @@ package com.android.server.hdmi;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_LONG;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
@@ -145,11 +146,12 @@ public class HdmiCecMessageValidatorTest {
@Test
public void isValid_systemAudioModeStatus() {
assertMessageValidity("40:7E:00").isEqualTo(OK);
- assertMessageValidity("40:7E:01:01").isEqualTo(OK);
+ assertMessageValidity("40:7E:01").isEqualTo(OK);
assertMessageValidity("0F:7E:00").isEqualTo(ERROR_DESTINATION);
assertMessageValidity("F0:7E").isEqualTo(ERROR_SOURCE);
assertMessageValidity("40:7E").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:7E:01:1F:28").isEqualTo(ERROR_PARAMETER_LONG);
assertMessageValidity("40:7E:02").isEqualTo(ERROR_PARAMETER);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 4dd5e94541e4..fd6eb9286651 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -761,6 +761,13 @@ public class HdmiControlServiceTest {
assertThat(mHdmiControlServiceSpy.handleCecCommand(message))
.isEqualTo(Constants.ABORT_INVALID_OPERAND);
+
+ // Validating ERROR_PARAMETER_LONG will generate ABORT_INVALID_OPERAND.
+ // Taken from HdmiCecMessageValidatorTest#isValid_systemAudioModeStatus
+ HdmiCecMessage systemAudioModeStatus = HdmiUtils.buildMessage("40:7E:01:1F:28");
+
+ assertThat(mHdmiControlServiceSpy.handleCecCommand(systemAudioModeStatus))
+ .isEqualTo(Constants.ABORT_INVALID_OPERAND);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 5636795520e9..3513557d8374 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -202,9 +202,11 @@ class TestPhoneWindowManager {
.canonicalToCurrentPackageNames(any());
} catch (PackageManager.NameNotFoundException ignored) { }
- doReturn(mTelecomManager).when(mContext).getSystemService(eq(Context.TELECOM_SERVICE));
- doReturn(mNotificationManager).when(mContext)
- .getSystemService(eq(NotificationManager.class));
+ doReturn(false).when(mTelecomManager).isInCall();
+ doReturn(false).when(mTelecomManager).isRinging();
+ doReturn(mTelecomManager).when(mPhoneWindowManager).getTelecommService();
+ doNothing().when(mNotificationManager).silenceNotificationSound();
+ doReturn(mNotificationManager).when(mPhoneWindowManager).getNotificationService();
doReturn(mVibrator).when(mContext).getSystemService(eq(Context.VIBRATOR_SERVICE));
final PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
@@ -235,6 +237,7 @@ class TestPhoneWindowManager {
doNothing().when(mPhoneWindowManager).screenTurnedOn(anyInt());
doNothing().when(mPhoneWindowManager).startedWakingUp(anyInt(), anyInt());
doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt(), anyInt());
+ doNothing().when(mPhoneWindowManager).lockNow(any());
mPhoneWindowManager.init(new TestInjector(mContext, mWindowManagerFuncsImpl));
mPhoneWindowManager.systemReady();
@@ -249,6 +252,7 @@ class TestPhoneWindowManager {
void tearDown() {
mHandlerThread.quitSafely();
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
+ Mockito.reset(mPhoneWindowManager);
mMockitoSession.finishMocking();
}
@@ -322,6 +326,7 @@ class TestPhoneWindowManager {
void overrideDisplayState(int state) {
doReturn(state).when(mDisplay).getState();
+ doReturn(state == STATE_ON).when(mDisplayPolicy).isAwake();
Mockito.reset(mPowerManager);
}
@@ -388,6 +393,7 @@ class TestPhoneWindowManager {
}
void assertDreamRequest() {
+ waitForIdle();
verify(mDreamManagerInternal).requestDream();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index dfc453f0f1b6..d173ce9d522a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -1134,7 +1134,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
TaskChangeNotificationController controller = mAtm.getTaskChangeNotificationController();
spyOn(controller);
mWm.mRoot.lockAllProfileTasks(profileUserId);
- verify(controller).notifyTaskProfileLocked(any());
+ verify(controller).notifyTaskProfileLocked(any(), eq(profileUserId));
// Create the work lock activity on top of the task
final ActivityRecord workLockActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1144,7 +1144,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
// Make sure the listener won't be notified again.
clearInvocations(controller);
mWm.mRoot.lockAllProfileTasks(profileUserId);
- verify(controller, never()).notifyTaskProfileLocked(any());
+ verify(controller, never()).notifyTaskProfileLocked(any(), anyInt());
}
/**
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index b062e6b08e00..913535e06a21 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -296,7 +296,10 @@ public class SoundTriggerService extends SystemService {
// Helper to add session logger to the capacity limited detached list.
// If we are at capacity, remove the oldest, and retry
- private void addDetachedSessionLogger(EventLogger logger) {
+ private void detachSessionLogger(EventLogger logger) {
+ if (!mSessionEventLoggers.remove(logger)) {
+ return;
+ }
// Attempt to push to the top of the queue
while (!mDetachedSessionEventLoggers.offerFirst(logger)) {
// Remove the oldest element, if one still exists
@@ -872,8 +875,7 @@ public class SoundTriggerService extends SystemService {
private void detach() {
mSoundTriggerHelper.detach();
- mSessionEventLoggers.remove(mEventLogger);
- addDetachedSessionLogger(mEventLogger);
+ detachSessionLogger(mEventLogger);
}
private void enforceCallingPermission(String permission) {
@@ -1659,8 +1661,7 @@ public class SoundTriggerService extends SystemService {
private void detachInternal() {
mEventLogger.enqueue(new SessionEvent(Type.DETACH, null));
- mSessionEventLoggers.remove(mEventLogger);
- addDetachedSessionLogger(mEventLogger);
+ detachSessionLogger(mEventLogger);
mSoundTriggerHelper.detach();
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 13fe14caa1f9..00cedd77414e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -78,7 +78,7 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
public @NonNull
SoundTriggerModuleDescriptor[] listModules() {
Identity identity = getIdentity();
- enforcePermissionsForPreflight(identity);
+ enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD);
return mDelegate.listModules();
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 486945d9159e..edaaf3fbbccc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -913,8 +913,10 @@ final class HotwordDetectionConnection {
}
// Handle case where all hotword detector sessions are destroyed with only the visual
// detector session left
- if (mDetectorSessions.size() == 1
- && mDetectorSessions.get(0) instanceof VisualQueryDetectorSession) {
+ boolean allHotwordDetectionServiceSessionsRemoved = mDetectorSessions.size() == 0
+ || (mDetectorSessions.size() == 1 && mDetectorSessions.get(0)
+ instanceof VisualQueryDetectorSession);
+ if (allHotwordDetectionServiceSessionsRemoved) {
unbindHotwordDetectionService();
}
}