summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java35
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java2
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java5
-rw-r--r--config/hiddenapi-unsupported.txt1
-rw-r--r--core/api/current.txt14
-rw-r--r--core/api/system-current.txt2
-rw-r--r--core/api/test-current.txt27
-rw-r--r--core/java/android/app/Notification.java18
-rw-r--r--core/java/android/app/SystemServiceRegistry.java9
-rw-r--r--core/java/android/app/backup/BackupManager.java61
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl6
-rw-r--r--core/java/android/app/time/ExternalTimeSuggestion.aidl (renamed from core/java/android/app/timedetector/ExternalTimeSuggestion.aidl)2
-rw-r--r--core/java/android/app/time/ExternalTimeSuggestion.java (renamed from core/java/android/app/timedetector/ExternalTimeSuggestion.java)2
-rw-r--r--core/java/android/app/timedetector/ITimeDetectorService.aidl2
-rw-r--r--core/java/android/app/timedetector/TimeDetector.java1
-rw-r--r--core/java/android/app/timedetector/TimeDetectorImpl.java1
-rw-r--r--core/java/android/content/Context.java24
-rw-r--r--core/java/android/content/Intent.java12
-rw-r--r--core/java/android/content/pm/DataLoaderManager.java5
-rw-r--r--core/java/android/content/pm/IDataLoaderManager.aidl2
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/PackageParser.java50
-rw-r--r--core/java/android/content/pm/PermissionInfo.java23
-rw-r--r--core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java1
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermission.java8
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java40
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java7
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java2
-rw-r--r--core/java/android/hardware/biometrics/BiometricTestSession.java9
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java2
-rw-r--r--core/java/android/hardware/input/InputDeviceVibrator.java5
-rw-r--r--core/java/android/hardware/input/InputDeviceVibratorManager.java14
-rw-r--r--core/java/android/os/BatteryStats.java26
-rw-r--r--core/java/android/os/CombinedVibrationEffect.java51
-rw-r--r--core/java/android/os/IVibratorService.aidl37
-rw-r--r--core/java/android/os/OWNERS1
-rw-r--r--core/java/android/os/SystemVibrator.java426
-rw-r--r--core/java/android/os/SystemVibratorManager.java360
-rw-r--r--core/java/android/os/Vibrator.java9
-rw-r--r--core/java/android/os/VibratorManager.java110
-rw-r--r--core/java/android/view/SurfaceSession.java15
-rw-r--r--core/java/android/view/SurfaceView.java89
-rw-r--r--core/java/android/view/ViewRootImpl.java32
-rw-r--r--core/java/android/widget/EdgeEffect.java95
-rw-r--r--core/java/com/android/internal/display/BrightnessSynchronizer.java (renamed from core/java/com/android/internal/BrightnessSynchronizer.java)2
-rw-r--r--core/java/com/android/internal/display/OWNERS3
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java91
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyArray.java66
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java25
-rw-r--r--core/jni/android_view_SurfaceSession.cpp7
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/values/attrs_manifest.xml14
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java (renamed from core/tests/coretests/src/android/app/timedetector/ExternalTimeSuggestionTest.java)2
-rw-r--r--core/tests/coretests/src/android/content/pm/SigningDetailsTest.java130
-rw-r--r--core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java34
-rw-r--r--core/tests/coretests/src/android/provider/OWNERS1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java119
-rw-r--r--core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java53
-rw-r--r--graphics/java/android/graphics/Typeface.java17
-rw-r--r--graphics/java/android/graphics/fonts/FontFamily.java5
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java6
-rw-r--r--libs/hwui/jni/Typeface.cpp50
-rw-r--r--libs/incident/Android.bp2
-rw-r--r--media/java/android/media/DrmInitData.java7
-rw-r--r--media/java/android/media/tv/TvInputManager.java2
-rw-r--r--media/jni/Android.bp4
-rw-r--r--media/jni/tuner/ClientHelper.h2
-rw-r--r--media/jni/tuner/FilterClient.cpp20
-rw-r--r--media/jni/tuner/FrontendClient.cpp171
-rw-r--r--media/jni/tuner/FrontendClient.h3
-rw-r--r--media/jni/tuner/TunerClient.cpp15
-rw-r--r--media/jni/tuner/TunerClient.h4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java4
-rw-r--r--packages/SystemUI/res/color/kg_user_avatar_frame.xml (renamed from packages/SystemUI/res/layout/keyguard_user_switcher_inner.xml)18
-rw-r--r--packages/SystemUI/res/drawable/end_guest_button_background.xml25
-rw-r--r--packages/SystemUI/res/drawable/kg_bg_avatar.xml28
-rw-r--r--packages/SystemUI/res/layout/keyguard_status_bar.xml17
-rw-r--r--packages/SystemUI/res/layout/keyguard_user_switcher.xml54
-rw-r--r--packages/SystemUI/res/layout/keyguard_user_switcher_item.xml41
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml12
-rw-r--r--packages/SystemUI/res/values-night/colors.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp/styles.xml7
-rw-r--r--packages/SystemUI/res/values/colors.xml10
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml13
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java94
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java137
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java24
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt157
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java113
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java184
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java414
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java639
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java168
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java340
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt267
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java2
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java23
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java51
-rw-r--r--services/core/java/com/android/server/OWNERS2
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java16
-rw-r--r--services/core/java/com/android/server/VibratorManagerService.java214
-rw-r--r--services/core/java/com/android/server/VibratorService.java1243
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java229
-rw-r--r--services/core/java/com/android/server/am/MeasuredEnergySnapshot.java272
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java33
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java36
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java7
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java2
-rw-r--r--services/core/java/com/android/server/display/BrightnessMappingStrategy.java2
-rw-r--r--services/core/java/com/android/server/display/DeviceStateToLayoutMap.java121
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceRepository.java12
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java26
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerState.java2
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java2
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java9
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java145
-rw-r--r--services/core/java/com/android/server/display/RampAnimator.java2
-rw-r--r--services/core/java/com/android/server/display/layout/Layout.java154
-rw-r--r--services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java33
-rw-r--r--services/core/java/com/android/server/lights/LightsService.java2
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java2
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java36
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java92
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java3
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/Permission.java9
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java5
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java2
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java23
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java2
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java2
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java13
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp4
-rw-r--r--services/incremental/IncrementalService.cpp84
-rw-r--r--services/incremental/IncrementalService.h8
-rw-r--r--services/incremental/ServiceWrappers.cpp3
-rw-r--r--services/incremental/ServiceWrappers.h2
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp118
-rw-r--r--services/java/com/android/server/SystemServer.java14
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorServiceTest.java757
-rw-r--r--services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java297
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java280
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java43
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java1
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java2
-rw-r--r--telephony/java/android/telephony/Annotation.java2
-rw-r--r--telephony/java/android/telephony/TelephonyDisplayInfo.java19
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java (renamed from tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java)32
237 files changed, 6578 insertions, 4666 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 82ee5d8c1c7c..71fe55fb0640 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -212,7 +212,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
final JobConcurrencyManager mConcurrencyManager;
- static final int MSG_JOB_EXPIRED = 0;
+ static final int MSG_CHECK_INDIVIDUAL_JOB = 0;
static final int MSG_CHECK_JOB = 1;
static final int MSG_STOP_JOB = 2;
static final int MSG_CHECK_JOB_GREEDY = 3;
@@ -1711,6 +1711,12 @@ public class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
+ JobStatus newJs = mJobs.getJobByUidAndJobId(jobStatus.getUid(), jobStatus.getJobId());
+ if (newJs != null) {
+ // This job was stopped because the app scheduled a new job with the same job ID.
+ // Check if the new job is ready to run.
+ mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, newJs).sendToTarget();
+ }
return;
}
@@ -1748,7 +1754,11 @@ public class JobSchedulerService extends com.android.server.SystemService
@Override
public void onRunJobNow(JobStatus jobStatus) {
- mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
+ if (jobStatus == null) {
+ mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
+ } else {
+ mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, jobStatus).sendToTarget();
+ }
}
final private class JobHandler extends Handler {
@@ -1764,18 +1774,15 @@ public class JobSchedulerService extends com.android.server.SystemService
return;
}
switch (message.what) {
- case MSG_JOB_EXPIRED: {
- JobStatus runNow = (JobStatus) message.obj;
- // runNow can be null, which is a controller's way of indicating that its
- // state is such that all ready jobs should be run immediately.
- if (runNow != null) {
- if (!isCurrentlyActiveLocked(runNow)
- && isReadyToBeExecutedLocked(runNow)) {
- mJobPackageTracker.notePending(runNow);
- addOrderedItem(mPendingJobs, runNow, sPendingJobComparator);
+ case MSG_CHECK_INDIVIDUAL_JOB: {
+ JobStatus js = (JobStatus) message.obj;
+ if (js != null) {
+ if (isReadyToBeExecutedLocked(js)) {
+ mJobPackageTracker.notePending(js);
+ addOrderedItem(mPendingJobs, js, sPendingJobComparator);
}
} else {
- queueReadyJobsForExecutionLocked();
+ Slog.e(TAG, "Given null job to check individually");
}
} break;
case MSG_CHECK_JOB:
@@ -1909,12 +1916,10 @@ public class JobSchedulerService extends com.android.server.SystemService
// This method will check and capture all ready jobs, so we don't need to keep any messages
// in the queue.
mHandler.removeMessages(MSG_CHECK_JOB_GREEDY);
+ mHandler.removeMessages(MSG_CHECK_INDIVIDUAL_JOB);
// MSG_CHECK_JOB is a weaker form of _GREEDY. Since we're checking and queueing all ready
// jobs, we don't need to keep any MSG_CHECK_JOB messages in the queue.
mHandler.removeMessages(MSG_CHECK_JOB);
- // This method will capture all expired jobs that are ready, so there's no need to keep
- // the _EXPIRED messages in the queue.
- mHandler.removeMessages(MSG_JOB_EXPIRED);
if (DEBUG) {
Slog.d(TAG, "queuing all ready jobs for execution:");
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index 50723c7c2841..131a6d4f4791 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -107,8 +107,6 @@ public final class ContentObserverController extends StateController {
taskStatus.contentObserverJobInstance.mChangedUris.add(uri);
}
}
- taskStatus.changedAuthorities = null;
- taskStatus.changedUris = null;
}
taskStatus.changedAuthorities = null;
taskStatus.changedUris = null;
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 4b7eda096e54..ed717c491467 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -19,7 +19,6 @@ package com.android.commands.bmgr;
import android.annotation.IntDef;
import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
-import android.app.backup.BackupManager.OperationType;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupProgress;
import android.app.backup.BackupTransport;
@@ -667,7 +666,7 @@ public class Bmgr {
// The rest of the 'list' options work with a restore session on the current transport
try {
- mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
+ mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
return;
@@ -822,7 +821,7 @@ public class Bmgr {
try {
boolean didRestore = false;
- mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
+ mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
return;
diff --git a/config/hiddenapi-unsupported.txt b/config/hiddenapi-unsupported.txt
index 90a526bcfaf7..48aa8b2c2e30 100644
--- a/config/hiddenapi-unsupported.txt
+++ b/config/hiddenapi-unsupported.txt
@@ -204,7 +204,6 @@ Landroid/os/IRemoteCallback$Stub;-><init>()V
Landroid/os/IUpdateEngine$Stub;-><init>()V
Landroid/os/IUserManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/IUserManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IUserManager;
-Landroid/os/IVibratorService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IVibratorService;
Landroid/os/storage/IObbActionListener$Stub;-><init>()V
Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
diff --git a/core/api/current.txt b/core/api/current.txt
index 104d933ce315..2531d818e528 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -849,6 +849,7 @@ package android {
field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
field public static final int keycode = 16842949; // 0x10100c5
field public static final int killAfterRestore = 16843420; // 0x101029c
+ field public static final int knownCerts = 16844330; // 0x101062a
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field @Deprecated public static final int labelTextSize = 16843317; // 0x1010235
@@ -10440,6 +10441,7 @@ package android.content {
field public static final String USAGE_STATS_SERVICE = "usagestats";
field public static final String USB_SERVICE = "usb";
field public static final String USER_SERVICE = "user";
+ field public static final String VIBRATOR_MANAGER_SERVICE = "vibrator_manager";
field public static final String VIBRATOR_SERVICE = "vibrator";
field public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
field public static final String WALLPAPER_SERVICE = "wallpaper";
@@ -10980,6 +10982,7 @@ package android.content {
field public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
field public static final String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME";
field public static final String EXTRA_INTENT = "android.intent.extra.INTENT";
+ field public static final String EXTRA_IS_BUBBLED = "android.intent.extra.IS_BUBBLED";
field public static final String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT";
field public static final String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY";
field public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
@@ -31627,6 +31630,7 @@ package android.os {
method @NonNull public int[] areEffectsSupported(@NonNull int...);
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
+ method public int getId();
method public abstract boolean hasAmplitudeControl();
method public abstract boolean hasVibrator();
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
@@ -31641,10 +31645,12 @@ package android.os {
}
public abstract class VibratorManager {
+ method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
method @NonNull public abstract android.os.Vibrator getDefaultVibrator();
method @NonNull public abstract android.os.Vibrator getVibrator(int);
method @NonNull public abstract int[] getVibratorIds();
- method public abstract void vibrate(@NonNull android.os.CombinedVibrationEffect);
+ method @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull android.os.CombinedVibrationEffect);
+ method @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull android.os.CombinedVibrationEffect, @Nullable android.os.VibrationAttributes);
}
public class WorkSource implements android.os.Parcelable {
@@ -42031,8 +42037,9 @@ package android.telephony {
field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2
field public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; // 0x1
field public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; // 0x0
+ field public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 4; // 0x4
field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; // 0x3
- field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
+ field @Deprecated public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
}
public class TelephonyManager {
@@ -53584,16 +53591,19 @@ package android.widget {
public class EdgeEffect {
ctor public EdgeEffect(android.content.Context);
+ ctor public EdgeEffect(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
method public boolean draw(android.graphics.Canvas);
method public void finish();
method @Nullable public android.graphics.BlendMode getBlendMode();
method @ColorInt public int getColor();
+ method public float getDistance();
method public int getMaxHeight();
method public int getType();
method public boolean isFinished();
method public void onAbsorb(int);
method public void onPull(float);
method public void onPull(float, float);
+ method public float onPullDistance(float, float);
method public void onRelease();
method public void setBlendMode(@Nullable android.graphics.BlendMode);
method public void setColor(@ColorInt int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index bfdce373a47e..fdd1e6660697 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -179,6 +179,7 @@ package android {
field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
field public static final String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
field public static final String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
+ field public static final String PERFORM_IMS_SINGLE_REGISTRATION = "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
@@ -2631,6 +2632,7 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
+ field public static final int PROTECTION_FLAG_KNOWN_SIGNER = 134217728; // 0x8000000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 06110319cb35..632b10f3330e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -693,6 +693,7 @@ package android.content.pm {
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
method public void holdLock(android.os.IBinder, int);
+ method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
@@ -1371,6 +1372,32 @@ package android.os {
field public static final int RESOURCES_SDK_INT;
}
+ public abstract class CombinedVibrationEffect implements android.os.Parcelable {
+ method public abstract long getDuration();
+ }
+
+ public static final class CombinedVibrationEffect.Mono extends android.os.CombinedVibrationEffect {
+ method public long getDuration();
+ method @NonNull public android.os.VibrationEffect getEffect();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Mono> CREATOR;
+ }
+
+ public static final class CombinedVibrationEffect.Sequential extends android.os.CombinedVibrationEffect {
+ method @NonNull public java.util.List<java.lang.Integer> getDelays();
+ method public long getDuration();
+ method @NonNull public java.util.List<android.os.CombinedVibrationEffect> getEffects();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Sequential> CREATOR;
+ }
+
+ public static final class CombinedVibrationEffect.Stereo extends android.os.CombinedVibrationEffect {
+ method public long getDuration();
+ method @NonNull public android.util.SparseArray<android.os.VibrationEffect> getEffects();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Stereo> CREATOR;
+ }
+
public class DeviceIdleManager {
method @NonNull public String[] getSystemPowerWhitelist();
method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c242fd466c41..050f34a21477 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9972,6 +9972,15 @@ public class Notification implements Parcelable
* <p>The shortcut activity will be used when the bubble is expanded. This will display
* the shortcut activity in a floating window over the existing foreground activity.</p>
*
+ * <p>When the shortcut is displayed in a bubble, there will be an intent
+ * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED}
+ * with {@code true}. You may check this in the onCreate of your activity via:
+ *
+ * <pre class="prettyprint">
+ * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false);
+ * </pre>
+ * </p>
+ *
* <p>If the shortcut has not been published when the bubble notification is sent,
* no bubble will be produced. If the shortcut is deleted while the bubble is active,
* the bubble will be removed.</p>
@@ -10000,6 +10009,15 @@ public class Notification implements Parcelable
* app content in a floating window over the existing foreground activity. The intent
* should point to a resizable activity. </p>
*
+ * <p>When the activity is displayed in a bubble, there will be an intent
+ * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED}
+ * with {@code true}. You may check this in the onCreate of your activity via:
+ *
+ * <pre class="prettyprint">
+ * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false);
+ * </pre>
+ * </p>
+ *
* @throws NullPointerException if intent is null.
* @throws NullPointerException if icon is null.
*/
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 7404e53bd8b3..d5e95708a805 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -167,9 +167,11 @@ import android.os.StatsFrameworkInitializer;
import android.os.SystemConfigManager;
import android.os.SystemUpdateManager;
import android.os.SystemVibrator;
+import android.os.SystemVibratorManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
+import android.os.VibratorManager;
import android.os.health.SystemHealthManager;
import android.os.image.DynamicSystemManager;
import android.os.image.IDynamicSystemService;
@@ -699,6 +701,13 @@ public final class SystemServiceRegistry {
}
});
+ registerService(Context.VIBRATOR_MANAGER_SERVICE, VibratorManager.class,
+ new CachedServiceFetcher<VibratorManager>() {
+ @Override
+ public VibratorManager createService(ContextImpl ctx) {
+ return new SystemVibratorManager(ctx);
+ }});
+
registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
new CachedServiceFetcher<Vibrator>() {
@Override
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 673de8fa7c8c..dae565e12fd7 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -361,36 +361,7 @@ public class BackupManager {
try {
// All packages, current transport
IRestoreSession binder =
- sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
- OperationType.BACKUP);
- if (binder != null) {
- session = new RestoreSession(mContext, binder);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "beginRestoreSession() couldn't connect");
- }
- }
- return session;
- }
-
- /**
- * Begin the process of restoring data from backup. See the
- * {@link android.app.backup.RestoreSession} class for documentation on that process.
- *
- * @param operationType Type of the operation, see {@link OperationType}
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BACKUP)
- public RestoreSession beginRestoreSession(@OperationType int operationType) {
- RestoreSession session = null;
- checkServiceBinder();
- if (sService != null) {
- try {
- // All packages, current transport
- IRestoreSession binder =
- sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
- operationType);
+ sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
if (binder != null) {
session = new RestoreSession(mContext, binder);
}
@@ -801,7 +772,7 @@ public class BackupManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.BACKUP)
public int requestBackup(String[] packages, BackupObserver observer) {
- return requestBackup(packages, observer, null, 0, OperationType.BACKUP);
+ return requestBackup(packages, observer, null, 0);
}
/**
@@ -826,31 +797,6 @@ public class BackupManager {
@RequiresPermission(android.Manifest.permission.BACKUP)
public int requestBackup(String[] packages, BackupObserver observer,
BackupManagerMonitor monitor, int flags) {
- return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
- }
-
- /**
- * Request an immediate backup, providing an observer to which results of the backup operation
- * will be published. The Android backup system will decide for each package whether it will
- * be full app data backup or key/value-pair-based backup.
- *
- * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
- * provided packages using the remote transport.
- *
- * @param packages List of package names to backup.
- * @param observer The {@link BackupObserver} to receive callbacks during the backup
- * operation. Could be {@code null}.
- * @param monitor The {@link BackupManagerMonitorWrapper} to receive callbacks of important
- * events during the backup operation. Could be {@code null}.
- * @param flags {@link #FLAG_NON_INCREMENTAL_BACKUP}.
- * @param operationType {@link OperationType}
- * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
- * @throws IllegalArgumentException on null or empty {@code packages} param.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BACKUP)
- public int requestBackup(String[] packages, BackupObserver observer,
- BackupManagerMonitor monitor, int flags, @OperationType int operationType) {
checkServiceBinder();
if (sService != null) {
try {
@@ -860,8 +806,7 @@ public class BackupManager {
BackupManagerMonitorWrapper monitorWrapper = monitor == null
? null
: new BackupManagerMonitorWrapper(monitor);
- return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags,
- operationType);
+ return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags);
} catch (RemoteException e) {
Log.e(TAG, "requestBackup() couldn't connect");
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index e1bbc08e72f3..bf5be95c4ab0 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -547,11 +547,9 @@ interface IBackupManager {
* set can be restored.
* @param transportID The name of the transport to use for the restore operation.
* May be null, in which case the current active transport is used.
- * @param operationType Type of the operation, see {@link BackupManager#OperationType}
* @return An interface to the restore session, or null on error.
*/
- IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID,
- int operationType);
+ IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
/**
* Notify the backup manager that a BackupAgent has completed the operation
@@ -680,7 +678,7 @@ interface IBackupManager {
* {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id.
*/
int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor,
- int flags, int operationType);
+ int flags);
/**
* Cancel all running backups. After this call returns, no currently running backups will
diff --git a/core/java/android/app/timedetector/ExternalTimeSuggestion.aidl b/core/java/android/app/time/ExternalTimeSuggestion.aidl
index 14d57bf463fb..07a0fbbcbe8e 100644
--- a/core/java/android/app/timedetector/ExternalTimeSuggestion.aidl
+++ b/core/java/android/app/time/ExternalTimeSuggestion.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.timedetector;
+package android.app.time;
parcelable ExternalTimeSuggestion;
diff --git a/core/java/android/app/timedetector/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java
index 7ad303a9a1c6..b566eab9867d 100644
--- a/core/java/android/app/timedetector/ExternalTimeSuggestion.java
+++ b/core/java/android/app/time/ExternalTimeSuggestion.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.timedetector;
+package android.app.time;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index 462654323cb4..c4546be10601 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -16,7 +16,7 @@
package android.app.timedetector;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index df8d797e5b90..76f378590ae2 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -19,6 +19,7 @@ package android.app.timedetector;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.app.time.ExternalTimeSuggestion;
import android.content.Context;
import android.os.SystemClock;
import android.os.TimestampedValue;
diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java
index f80869f0a4fc..ef818ef647d6 100644
--- a/core/java/android/app/timedetector/TimeDetectorImpl.java
+++ b/core/java/android/app/timedetector/TimeDetectorImpl.java
@@ -17,6 +17,7 @@
package android.app.timedetector;
import android.annotation.NonNull;
+import android.app.time.ExternalTimeSuggestion;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2a402b204cb7..025d777f2053 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3479,6 +3479,7 @@ public abstract class Context {
STORAGE_STATS_SERVICE,
WALLPAPER_SERVICE,
TIME_ZONE_RULES_MANAGER_SERVICE,
+ VIBRATOR_MANAGER_SERVICE,
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
@@ -3625,9 +3626,11 @@ public abstract class Context {
* (e.g., GPS) updates.
* <dt> {@link #SEARCH_SERVICE} ("search")
* <dd> A {@link android.app.SearchManager} for handling search.
+ * <dt> {@link #VIBRATOR_MANAGER_SERVICE} ("vibrator_manager")
+ * <dd> A {@link android.os.VibratorManager} for accessing the device vibrators, interacting
+ * with individual ones and playing synchronized effects on multiple vibrators.
* <dt> {@link #VIBRATOR_SERVICE} ("vibrator")
- * <dd> A {@link android.os.Vibrator} for interacting with the vibrator
- * hardware.
+ * <dd> A {@link android.os.Vibrator} for interacting with the vibrator hardware.
* <dt> {@link #CONNECTIVITY_SERVICE} ("connectivity")
* <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
* handling management of network connections.
@@ -3707,6 +3710,8 @@ public abstract class Context {
* @see android.hardware.SensorManager
* @see #STORAGE_SERVICE
* @see android.os.storage.StorageManager
+ * @see #VIBRATOR_MANAGER_SERVICE
+ * @see android.os.VibratorManager
* @see #VIBRATOR_SERVICE
* @see android.os.Vibrator
* @see #CONNECTIVITY_SERVICE
@@ -4034,8 +4039,19 @@ public abstract class Context {
public static final String WALLPAPER_SERVICE = "wallpaper";
/**
- * Use with {@link #getSystemService(String)} to retrieve a {@link
- * android.os.Vibrator} for interacting with the vibration hardware.
+ * Use with {@link #getSystemService(String)} to retrieve a {@link android.os.VibratorManager}
+ * for accessing the device vibrators, interacting with individual ones and playing synchronized
+ * effects on multiple vibrators.
+ *
+ * @see #getSystemService(String)
+ * @see android.os.VibratorManager
+ */
+ @SuppressLint("ServiceName")
+ public static final String VIBRATOR_MANAGER_SERVICE = "vibrator_manager";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link android.os.Vibrator} for
+ * interacting with the vibration hardware.
*
* @see #getSystemService(String)
* @see android.os.Vibrator
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 30b24044a624..4abd8cd7d37b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5718,7 +5718,7 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
/**
- * Used in the extra field in the remote intent. It's astring token passed with the
+ * Used in the extra field in the remote intent. It's a string token passed with the
* remote intent.
*/
public static final String EXTRA_REMOTE_INTENT_TOKEN =
@@ -6062,6 +6062,16 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
+ /**
+ * A boolean extra indicating whether an activity is bubbled. Set on the shortcut or
+ * pending intent provided for the bubble. If the extra is not present or false, then it is not
+ * bubbled.
+ *
+ * @see android.app.Notification.Builder#setBubbleMetadata(Notification.BubbleMetadata)
+ * @see android.app.Notification.BubbleMetadata.Builder#Builder(String)
+ */
+ public static final String EXTRA_IS_BUBBLED = "android.intent.extra.IS_BUBBLED";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/content/pm/DataLoaderManager.java b/core/java/android/content/pm/DataLoaderManager.java
index e8fb2413bbbe..4d79936a8b22 100644
--- a/core/java/android/content/pm/DataLoaderManager.java
+++ b/core/java/android/content/pm/DataLoaderManager.java
@@ -41,6 +41,7 @@ public class DataLoaderManager {
* @param dataLoaderId ID for the new data loader binder service.
* @param params DataLoaderParamsParcel object that contains data loader params, including
* its package name, class name, and additional parameters.
+ * @param bindDelayMs introduce a delay before actual bind in case we want to avoid busylooping
* @param listener Callback for the data loader service to report status back to the
* caller.
* @return false if 1) target ID collides with a data loader that is already bound to data
@@ -48,9 +49,9 @@ public class DataLoaderManager {
* or 4) fails to bind to the specified data loader service, otherwise return true.
*/
public boolean bindToDataLoader(int dataLoaderId, @NonNull DataLoaderParamsParcel params,
- @NonNull IDataLoaderStatusListener listener) {
+ long bindDelayMs, @NonNull IDataLoaderStatusListener listener) {
try {
- return mService.bindToDataLoader(dataLoaderId, params, listener);
+ return mService.bindToDataLoader(dataLoaderId, params, bindDelayMs, listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/IDataLoaderManager.aidl b/core/java/android/content/pm/IDataLoaderManager.aidl
index 93b3de7897c4..dda4d36d1217 100644
--- a/core/java/android/content/pm/IDataLoaderManager.aidl
+++ b/core/java/android/content/pm/IDataLoaderManager.aidl
@@ -23,7 +23,7 @@ import android.content.pm.IDataLoaderStatusListener;
/** @hide */
interface IDataLoaderManager {
- boolean bindToDataLoader(int id, in DataLoaderParamsParcel params,
+ boolean bindToDataLoader(int id, in DataLoaderParamsParcel params, long bindDelayMs,
IDataLoaderStatusListener listener);
IDataLoader getDataLoader(int dataLoaderId);
void unbindFromDataLoader(int dataLoaderId);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a46876ec53c4..7fe2a41cc4b9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -808,4 +808,6 @@ interface IPackageManager {
PackageManager.Property getProperty(String propertyName, String packageName, String className);
ParceledListSlice queryProperty(String propertyName, int componentType);
+
+ void setKeepUninstalledPackages(in List<String> packageList);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b95b991b095c..6cfcce3f7661 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,8 +47,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.dex.ArtManager;
+import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -9282,4 +9282,20 @@ public abstract class PackageManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Set a list of apps to keep around as APKs even if no user has currently installed it.
+ * @param packageList List of package names to keep cached.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES)
+ @TestApi
+ public void setKeepUninstalledPackages(@NonNull List<String> packageList) {
+ try {
+ ActivityThread.getPackageManager().setKeepUninstalledPackages(packageList);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0819d1743ad6..bf8d1f6ab07b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6151,6 +6151,56 @@ public class PackageParser {
}
/**
+ * Returns whether this instance is currently signed, or has ever been signed, with a
+ * signing certificate from the provided {@link Set} of {@code certDigests}.
+ *
+ * <p>The provided {@code certDigests} should contain the SHA-256 digest of the DER encoding
+ * of each trusted certificate with the digest characters in upper case. If this instance
+ * has multiple signers then all signers must be in the provided {@code Set}. If this
+ * instance has a signing lineage then this method will return true if any of the previous
+ * signers in the lineage match one of the entries in the {@code Set}.
+ */
+ public boolean hasAncestorOrSelfWithDigest(Set<String> certDigests) {
+ if (this == UNKNOWN || certDigests == null || certDigests.size() == 0) {
+ return false;
+ }
+ // If an app is signed by multiple signers then all of the signers must be in the Set.
+ if (signatures.length > 1) {
+ // If the Set has less elements than the number of signatures then immediately
+ // return false as there's no way to satisfy the requirement of all signatures being
+ // in the Set.
+ if (certDigests.size() < signatures.length) {
+ return false;
+ }
+ for (Signature signature : signatures) {
+ String signatureDigest = PackageUtils.computeSha256Digest(
+ signature.toByteArray());
+ if (!certDigests.contains(signatureDigest)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ String signatureDigest = PackageUtils.computeSha256Digest(signatures[0].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // The last element in the pastSigningCertificates array is the current signer;
+ // since that was verified above just check all the signers in the lineage.
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ signatureDigest = PackageUtils.computeSha256Digest(
+ pastSigningCertificates[i].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns the SigningDetails with a descendant (or same) signer after verifying the
* descendant has the same, a superset, or a subset of the lineage of the ancestor.
*
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 35f02a8d5dd2..a2e533af64a0 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -30,6 +30,7 @@ import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
/**
* Information you can retrieve about a particular security permission
@@ -278,6 +279,15 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
@SystemApi
public static final int PROTECTION_FLAG_ROLE = 0x4000000;
+ /**
+ * Additional flag for {@link #protectionLevel}, correspoinding to the {@code knownSigner} value
+ * of {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PROTECTION_FLAG_KNOWN_SIGNER = 0x8000000;
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -303,6 +313,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
PROTECTION_FLAG_RETAIL_DEMO,
PROTECTION_FLAG_RECENTS,
PROTECTION_FLAG_ROLE,
+ PROTECTION_FLAG_KNOWN_SIGNER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -466,6 +477,15 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
*/
public @Nullable CharSequence nonLocalizedDescription;
+ /**
+ * A {@link Set} of trusted signing certificate digests. If this permission has the {@link
+ * #PROTECTION_FLAG_KNOWN_SIGNER} flag set the permission will be granted to a requesting app
+ * if the app is signed by any of these certificates.
+ *
+ * @hide
+ */
+ public @Nullable Set<String> knownCerts;
+
/** @hide */
public static int fixProtectionLevel(int level) {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -570,6 +590,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_ROLE) != 0) {
protLevel.append("|role");
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0) {
+ protLevel.append("|knownSigner");
+ }
return protLevel.toString();
}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index fb0d90490567..9a84ded99c67 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -655,6 +655,7 @@ public class PackageInfoWithoutStateUtils {
pi.protectionLevel = p.getProtectionLevel();
pi.descriptionRes = p.getDescriptionRes();
pi.flags = p.getFlags();
+ pi.knownCerts = p.getKnownCerts();
if ((flags & PackageManager.GET_META_DATA) == 0) {
return pi;
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index f99a0b1dcadb..35bb33c84d56 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -26,6 +26,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import java.util.Set;
+
/** @hide */
public class ParsedPermission extends ParsedComponent {
@@ -39,6 +41,8 @@ public class ParsedPermission extends ParsedComponent {
boolean tree;
@Nullable
private ParsedPermissionGroup parsedPermissionGroup;
+ @Nullable
+ Set<String> knownCerts;
@VisibleForTesting
public ParsedPermission() {
@@ -81,6 +85,10 @@ public class ParsedPermission extends ParsedComponent {
return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
}
+ public @Nullable Set<String> getKnownCerts() {
+ return knownCerts;
+ }
+
public int calculateFootprint() {
int size = getName().length();
if (getNonLocalizedLabel() != null) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index 9012b5ce2b1e..a7cecbee8aec 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -25,6 +25,7 @@ import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.R;
@@ -32,6 +33,8 @@ import com.android.internal.R;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.Locale;
+import java.util.Set;
/** @hide */
public class ParsedPermissionUtils {
@@ -90,6 +93,43 @@ public class ParsedPermissionUtils {
permission.flags = sa.getInt(
R.styleable.AndroidManifestPermission_permissionFlags, 0);
+ final int knownCertsResource = sa.getResourceId(
+ R.styleable.AndroidManifestPermission_knownCerts, 0);
+ if (knownCertsResource != 0) {
+ // The knownCerts attribute supports both a string array resource as well as a
+ // string resource for the case where the permission should only be granted to a
+ // single known signer.
+ final String resourceType = res.getResourceTypeName(knownCertsResource);
+ if (resourceType.equals("array")) {
+ final String[] knownCerts = res.getStringArray(knownCertsResource);
+ if (knownCerts != null) {
+ // Convert the provided digest to upper case for consistent Set membership
+ // checks when verifying the signing certificate digests of requesting apps.
+ permission.knownCerts = new ArraySet<>();
+ for (String knownCert : knownCerts) {
+ permission.knownCerts.add(knownCert.toUpperCase(Locale.US));
+ }
+ }
+ } else {
+ final String knownCert = res.getString(knownCertsResource);
+ if (knownCert != null) {
+ permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+ }
+ }
+ if (permission.knownCerts == null) {
+ Slog.w(TAG, packageName + " defines a knownSigner permission but"
+ + " the provided knownCerts resource is null");
+ }
+ } else {
+ // If the knownCerts resource ID is null check if the app specified a string
+ // value for the attribute representing a single trusted signer.
+ final String knownCert = sa.getString(
+ R.styleable.AndroidManifestPermission_knownCerts);
+ if (knownCert != null) {
+ permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+ }
+ }
+
// For now only platform runtime permissions can be restricted
if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) {
permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index abf694f9742e..bbde8b103ef3 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -252,9 +252,10 @@ public class CompatibilityInfo implements Parcelable {
}
if (overrideScale != 1.0f) {
- applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
applicationScale = overrideScale;
applicationInvertedScale = 1.0f / overrideScale;
+ applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE
+ * applicationInvertedScale) + .5f);
compatFlags |= HAS_OVERRIDE_SCALING;
} else if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
applicationDensity = DisplayMetrics.DENSITY_DEVICE;
@@ -519,10 +520,6 @@ public class CompatibilityInfo implements Parcelable {
if (isScalingRequired()) {
float invertedRatio = applicationInvertedScale;
inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
- inoutConfig.screenWidthDp = (int) ((inoutConfig.screenWidthDp * invertedRatio) + .5f);
- inoutConfig.screenHeightDp = (int) ((inoutConfig.screenHeightDp * invertedRatio) + .5f);
- inoutConfig.smallestScreenWidthDp =
- (int) ((inoutConfig.smallestScreenWidthDp * invertedRatio) + .5f);
inoutConfig.windowConfiguration.getMaxBounds().scale(invertedRatio);
inoutConfig.windowConfiguration.getBounds().scale(invertedRatio);
final Rect appBounds = inoutConfig.windowConfiguration.getAppBounds();
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 08b1e245dc83..5f5697a4ed0b 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -236,7 +236,7 @@ public class BiometricManager {
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext,
+ return new BiometricTestSession(mContext, sensorId,
mService.createTestSession(sensorId, mContext.getOpPackageName()));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
index 2b689899af01..1c3560882f1b 100644
--- a/core/java/android/hardware/biometrics/BiometricTestSession.java
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.os.RemoteException;
import android.util.ArraySet;
+import android.util.Log;
/**
* Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
@@ -33,7 +34,10 @@ import android.util.ArraySet;
*/
@TestApi
public class BiometricTestSession implements AutoCloseable {
+ private static final String TAG = "BiometricTestSession";
+
private final Context mContext;
+ private final int mSensorId;
private final ITestSession mTestSession;
// Keep track of users that were tested, which need to be cleaned up when finishing.
@@ -42,8 +46,10 @@ public class BiometricTestSession implements AutoCloseable {
/**
* @hide
*/
- public BiometricTestSession(@NonNull Context context, @NonNull ITestSession testSession) {
+ public BiometricTestSession(@NonNull Context context, int sensorId,
+ @NonNull ITestSession testSession) {
mContext = context;
+ mSensorId = sensorId;
mTestSession = testSession;
mTestedUsers = new ArraySet<>();
setTestHalEnabled(true);
@@ -61,6 +67,7 @@ public class BiometricTestSession implements AutoCloseable {
@RequiresPermission(TEST_BIOMETRIC)
private void setTestHalEnabled(boolean enabled) {
try {
+ Log.w(TAG, "setTestHalEnabled, sensor: " + mSensorId + " enabled: " + enabled);
mTestSession.setTestHalEnabled(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 188a2a47fca0..a614ebfe1793 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -153,7 +153,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext,
+ return new BiometricTestSession(mContext, sensorId,
mService.createTestSession(sensorId, mContext.getOpPackageName()));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index f4d8a65d54c6..a4817ae27fa5 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -74,6 +74,11 @@ final class InputDeviceVibrator extends Vibrator {
}
@Override
+ public int getId() {
+ return mVibratorId;
+ }
+
+ @Override
public boolean hasVibrator() {
return true;
}
diff --git a/core/java/android/hardware/input/InputDeviceVibratorManager.java b/core/java/android/hardware/input/InputDeviceVibratorManager.java
index a381b02ab2a6..d843407c289d 100644
--- a/core/java/android/hardware/input/InputDeviceVibratorManager.java
+++ b/core/java/android/hardware/input/InputDeviceVibratorManager.java
@@ -16,9 +16,12 @@
package android.hardware.input;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Binder;
import android.os.CombinedVibrationEffect;
import android.os.NullVibrator;
+import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.util.SparseArray;
@@ -86,6 +89,7 @@ public class InputDeviceVibratorManager extends VibratorManager
}
}
+ @NonNull
@Override
public int[] getVibratorIds() {
synchronized (mVibrators) {
@@ -97,6 +101,7 @@ public class InputDeviceVibratorManager extends VibratorManager
}
}
+ @NonNull
@Override
public Vibrator getVibrator(int vibratorId) {
synchronized (mVibrators) {
@@ -107,6 +112,7 @@ public class InputDeviceVibratorManager extends VibratorManager
return NullVibrator.getInstance();
}
+ @NonNull
@Override
public Vibrator getDefaultVibrator() {
// Returns vibrator ID 0
@@ -119,7 +125,13 @@ public class InputDeviceVibratorManager extends VibratorManager
}
@Override
- public void vibrate(CombinedVibrationEffect effect) {
+ public void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
+ String reason, @Nullable VibrationAttributes attributes) {
mInputManager.vibrate(mDeviceId, effect, mToken);
}
+
+ @Override
+ public void cancel() {
+ mInputManager.cancelVibrate(mDeviceId, mToken);
+ }
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cc86a604c194..6f89254d9388 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -994,6 +994,19 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract long getScreenOnEnergy();
+ /**
+ * Returns the energies used by this uid for each
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
+ * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ *
+ * @return energies (in microjoules) used since boot for each (custom) energy consumer of
+ * type OTHER, indexed by their ordinal. Returns null if no energy reporting is
+ * supported.
+ *
+ * {@hide}
+ */
+ public abstract @Nullable long[] getCustomMeasuredEnergiesMicroJoules();
+
public static abstract class Sensor {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -2511,6 +2524,19 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract long getScreenDozeEnergy();
+ /**
+ * Returns the energies used for each
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
+ * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ *
+ * @return energies (in microjoules) used since boot for each (custom) energy consumer of
+ * type OTHER, indexed by their ordinal. Returns null if no energy reporting is
+ * supported.
+ *
+ * {@hide}
+ */
+ public abstract @Nullable long[] getCustomMeasuredEnergiesMicroJoules();
+
public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] {
new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index cb4e9cba0977..c8e682c86ea7 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
@@ -86,7 +87,20 @@ public abstract class CombinedVibrationEffect implements Parcelable {
return 0;
}
- /** @hide */
+ /**
+ * Gets the estimated duration of the combined vibration in milliseconds.
+ *
+ * <p>For synced combinations this means the maximum duration of any individual {@link
+ * VibrationEffect}. For sequential combinations, this is a sum of each step and delays.
+ *
+ * <p>For combinations of effects without a defined end (e.g. a Waveform with a non-negative
+ * repeat index), this returns Long.MAX_VALUE. For effects with an unknown duration (e.g.
+ * Prebaked effects where the length is device and potentially run-time dependent), this returns
+ * -1.
+ *
+ * @hide
+ */
+ @TestApi
public abstract long getDuration();
/** @hide */
@@ -256,6 +270,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
*
* @hide
*/
+ @TestApi
public static final class Mono extends CombinedVibrationEffect {
private final VibrationEffect mEffect;
@@ -267,6 +282,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
mEffect = effect;
}
+ @NonNull
public VibrationEffect getEffect() {
return mEffect;
}
@@ -282,6 +298,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
mEffect.validate();
}
+ /** @hide */
@Override
public boolean hasVibrator(int vibratorId) {
return true;
@@ -307,7 +324,12 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_MONO);
mEffect.writeToParcel(out, flags);
}
@@ -335,6 +357,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
*
* @hide
*/
+ @TestApi
public static final class Stereo extends CombinedVibrationEffect {
/** Mapping vibrator ids to effects. */
@@ -357,6 +380,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
/** Effects to be performed in sync, where each key represents the vibrator id. */
+ @NonNull
public SparseArray<VibrationEffect> getEffects() {
return mEffects;
}
@@ -394,6 +418,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
}
+ /** @hide */
@Override
public boolean hasVibrator(int vibratorId) {
return mEffects.indexOfKey(vibratorId) >= 0;
@@ -418,7 +443,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mEffects);
+ return mEffects.contentHashCode();
}
@Override
@@ -427,7 +452,12 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_STEREO);
out.writeInt(mEffects.size());
for (int i = 0; i < mEffects.size(); i++) {
@@ -459,6 +489,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
*
* @hide
*/
+ @TestApi
public static final class Sequential extends CombinedVibrationEffect {
private final List<CombinedVibrationEffect> mEffects;
private final List<Integer> mDelays;
@@ -480,11 +511,13 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
/** Effects to be performed in sequence. */
+ @NonNull
public List<CombinedVibrationEffect> getEffects() {
return mEffects;
}
/** Delay to be applied before each effect in {@link #getEffects()}. */
+ @NonNull
public List<Integer> getDelays() {
return mDelays;
}
@@ -542,6 +575,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
}
+ /** @hide */
@Override
public boolean hasVibrator(int vibratorId) {
final int effectCount = mEffects.size();
@@ -564,7 +598,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mEffects);
+ return Objects.hash(mEffects, mDelays);
}
@Override
@@ -573,7 +607,12 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_SEQUENTIAL);
out.writeInt(mEffects.size());
for (int i = 0; i < mEffects.size(); i++) {
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
deleted file mode 100644
index 1cd48dcf797b..000000000000
--- a/core/java/android/os/IVibratorService.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2007, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.os.VibrationEffect;
-import android.os.VibrationAttributes;
-import android.os.VibratorInfo;
-import android.os.IVibratorStateListener;
-
-/** {@hide} */
-interface IVibratorService
-{
- boolean hasVibrator();
- boolean isVibrating();
- VibratorInfo getVibratorInfo();
- boolean registerVibratorStateListener(in IVibratorStateListener listener);
- boolean unregisterVibratorStateListener(in IVibratorStateListener listener);
- boolean hasAmplitudeControl();
- void vibrate(int uid, String opPkg, in VibrationEffect effect,
- in VibrationAttributes attributes, String reason, IBinder token);
- void cancelVibrate(IBinder token);
-}
-
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 2559a33c1ab2..dac1edea7d3e 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -4,7 +4,6 @@ per-file ExternalVibration.java = michaelwr@google.com
per-file IExternalVibrationController.aidl = michaelwr@google.com
per-file IExternalVibratorService.aidl = michaelwr@google.com
per-file IVibratorManagerService.aidl = michaelwr@google.com
-per-file IVibratorService.aidl = michaelwr@google.com
per-file NullVibrator.java = michaelwr@google.com
per-file SystemVibrator.java = michaelwr@google.com
per-file VibrationEffect.aidl = michaelwr@google.com
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 30afe38be397..b42a495ece56 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -17,19 +17,18 @@
package android.os;
import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioAttributes;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -41,234 +40,178 @@ import java.util.concurrent.Executor;
public class SystemVibrator extends Vibrator {
private static final String TAG = "Vibrator";
- private static final int VIBRATOR_PRESENT_UNKNOWN = 0;
- private static final int VIBRATOR_PRESENT_YES = 1;
- private static final int VIBRATOR_PRESENT_NO = 2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({VIBRATOR_PRESENT_UNKNOWN, VIBRATOR_PRESENT_YES, VIBRATOR_PRESENT_NO})
- private @interface VibratorPresent {}
-
- private final IVibratorService mService;
- private final IVibratorManagerService mManagerService;
- private final Object mLock = new Object();
- private final Binder mToken = new Binder();
+ private final VibratorManager mVibratorManager;
private final Context mContext;
- @GuardedBy("mLock")
- private VibratorInfo mVibratorInfo;
- @GuardedBy("mLock")
- @VibratorPresent
- private int mVibratorPresent;
- @GuardedBy("mDelegates")
- private final ArrayMap<OnVibratorStateChangedListener,
- OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
+ @GuardedBy("mBrokenListeners")
+ private final ArrayList<AllVibratorsStateListener> mBrokenListeners = new ArrayList<>();
- @UnsupportedAppUsage
- public SystemVibrator() {
- mContext = null;
- mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
- mManagerService = IVibratorManagerService.Stub.asInterface(
- ServiceManager.getService("vibrator_manager"));
- }
+ @GuardedBy("mRegisteredListeners")
+ private final ArrayMap<OnVibratorStateChangedListener, AllVibratorsStateListener>
+ mRegisteredListeners = new ArrayMap<>();
@UnsupportedAppUsage
public SystemVibrator(Context context) {
super(context);
mContext = context;
- mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
- mManagerService = IVibratorManagerService.Stub.asInterface(
- ServiceManager.getService("vibrator_manager"));
+ mVibratorManager = mContext.getSystemService(VibratorManager.class);
}
@Override
public boolean hasVibrator() {
- try {
- synchronized (mLock) {
- if (mVibratorPresent == VIBRATOR_PRESENT_UNKNOWN && mService != null) {
- mVibratorPresent =
- mService.hasVibrator() ? VIBRATOR_PRESENT_YES : VIBRATOR_PRESENT_NO;
- }
- return mVibratorPresent == VIBRATOR_PRESENT_YES;
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to query vibrator presence", e);
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check if vibrator exists; no vibrator manager.");
return false;
}
+ return mVibratorManager.getVibratorIds().length > 0;
}
- /**
- * Check whether the vibrator is vibrating.
- *
- * @return True if the hardware is vibrating, otherwise false.
- */
@Override
public boolean isVibrating() {
- if (mService == null) {
- Log.w(TAG, "Failed to vibrate; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator manager.");
return false;
}
- try {
- return mService.isVibrating();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ for (int vibratorId : mVibratorManager.getVibratorIds()) {
+ if (mVibratorManager.getVibrator(vibratorId).isVibrating()) {
+ return true;
+ }
}
return false;
}
- private class OnVibratorStateChangedListenerDelegate extends
- IVibratorStateListener.Stub {
- private final Executor mExecutor;
- private final OnVibratorStateChangedListener mListener;
-
- OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
- @NonNull Executor executor) {
- mExecutor = executor;
- mListener = listener;
- }
-
- @Override
- public void onVibrating(boolean isVibrating) {
- mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+ @Override
+ public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ if (mContext == null) {
+ Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
+ return;
}
+ addVibratorStateListener(mContext.getMainExecutor(), listener);
}
- /**
- * Adds a listener for vibrator state change. If the listener was previously added and not
- * removed, this call will be ignored.
- *
- * @param listener Listener to be added.
- * @param executor The {@link Executor} on which the listener's callbacks will be executed on.
- */
@Override
public void addVibratorStateListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnVibratorStateChangedListener listener) {
Objects.requireNonNull(listener);
Objects.requireNonNull(executor);
- if (mService == null) {
- Log.w(TAG, "Failed to add vibrate state listener; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to add vibrate state listener; no vibrator manager.");
return;
}
-
- synchronized (mDelegates) {
- // If listener is already registered, reject and return.
- if (mDelegates.containsKey(listener)) {
- Log.w(TAG, "Listener already registered.");
- return;
- }
- try {
- final OnVibratorStateChangedListenerDelegate delegate =
- new OnVibratorStateChangedListenerDelegate(listener, executor);
- if (!mService.registerVibratorStateListener(delegate)) {
- Log.w(TAG, "Failed to register vibrate state listener");
+ AllVibratorsStateListener delegate = null;
+ try {
+ synchronized (mRegisteredListeners) {
+ // If listener is already registered, reject and return.
+ if (mRegisteredListeners.containsKey(listener)) {
+ Log.w(TAG, "Listener already registered.");
return;
}
- mDelegates.put(listener, delegate);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ delegate = new AllVibratorsStateListener(executor, listener);
+ delegate.register(mVibratorManager);
+ mRegisteredListeners.put(listener, delegate);
+ delegate = null;
}
+ } finally {
+ if (delegate != null && delegate.hasRegisteredListeners()) {
+ // The delegate listener was left in a partial state with listeners registered to
+ // some but not all vibrators. Keep track of this to try to unregister them later.
+ synchronized (mBrokenListeners) {
+ mBrokenListeners.add(delegate);
+ }
+ }
+ tryUnregisterBrokenListeners();
}
}
- /**
- * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
- * If the listener was previously added and not removed, this call will be ignored.
- *
- * @param listener listener to be added
- */
- @Override
- public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
- Objects.requireNonNull(listener);
- if (mContext == null) {
- Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
- return;
- }
- addVibratorStateListener(mContext.getMainExecutor(), listener);
- }
-
- /**
- * Removes the listener for vibrator state changes. If the listener was not previously
- * registered, this call will do nothing.
- *
- * @param listener Listener to be removed.
- */
@Override
public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
Objects.requireNonNull(listener);
- if (mService == null) {
- Log.w(TAG, "Failed to remove vibrate state listener; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to remove vibrate state listener; no vibrator manager.");
return;
}
- synchronized (mDelegates) {
- // Check if the listener is registered, otherwise will return.
- if (mDelegates.containsKey(listener)) {
- final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
- try {
- if (!mService.unregisterVibratorStateListener(delegate)) {
- Log.w(TAG, "Failed to unregister vibrate state listener");
- return;
- }
- mDelegates.remove(listener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ synchronized (mRegisteredListeners) {
+ if (mRegisteredListeners.containsKey(listener)) {
+ AllVibratorsStateListener delegate = mRegisteredListeners.get(listener);
+ delegate.unregister(mVibratorManager);
+ mRegisteredListeners.remove(listener);
}
}
+ tryUnregisterBrokenListeners();
}
@Override
public boolean hasAmplitudeControl() {
- if (mService == null) {
- Log.w(TAG, "Failed to check amplitude control; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check vibrator has amplitude control; no vibrator manager.");
return false;
}
- try {
- return mService.hasAmplitudeControl();
- } catch (RemoteException e) {
+ int[] vibratorIds = mVibratorManager.getVibratorIds();
+ if (vibratorIds.length == 0) {
+ return false;
}
- return false;
+ for (int vibratorId : vibratorIds) {
+ if (!mVibratorManager.getVibrator(vibratorId).hasAmplitudeControl()) {
+ return false;
+ }
+ }
+ return true;
}
@Override
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
AudioAttributes attributes) {
- if (mManagerService == null) {
- Log.w(TAG, "Failed to set always-on effect; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to set always-on effect; no vibrator manager.");
return false;
}
- try {
- VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
- CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
- return mManagerService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combinedEffect, atr);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to set always-on effect.", e);
- }
- return false;
+ VibrationAttributes attr = new VibrationAttributes.Builder(attributes, effect).build();
+ CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
+ return mVibratorManager.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combinedEffect, attr);
}
@Override
public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect,
String reason, @NonNull VibrationAttributes attributes) {
- if (mService == null) {
- Log.w(TAG, "Failed to vibrate; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator manager.");
return;
}
- try {
- mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to vibrate.", e);
- }
+ CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
+ mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes);
}
@Override
public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) {
- VibratorInfo vibratorInfo = getVibratorInfo();
int[] supported = new int[effectIds.length];
- for (int i = 0; i < effectIds.length; i++) {
- supported[i] = vibratorInfo == null
- ? Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN
- : vibratorInfo.isEffectSupported(effectIds[i]);
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check supported effects; no vibrator manager.");
+ Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
+ return supported;
+ }
+ int[] vibratorIds = mVibratorManager.getVibratorIds();
+ if (vibratorIds.length == 0) {
+ Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
+ return supported;
+ }
+ int[][] vibratorSupportMap = new int[vibratorIds.length][effectIds.length];
+ for (int i = 0; i < vibratorIds.length; i++) {
+ vibratorSupportMap[i] = mVibratorManager.getVibrator(
+ vibratorIds[i]).areEffectsSupported(effectIds);
+ }
+ Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_YES);
+ for (int effectIdx = 0; effectIdx < effectIds.length; effectIdx++) {
+ for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
+ int effectSupported = vibratorSupportMap[vibratorIdx][effectIdx];
+ if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
+ supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
+ break;
+ } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
+ supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+ }
+ }
}
return supported;
}
@@ -276,42 +219,169 @@ public class SystemVibrator extends Vibrator {
@Override
public boolean[] arePrimitivesSupported(
@NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
- VibratorInfo vibratorInfo = getVibratorInfo();
boolean[] supported = new boolean[primitiveIds.length];
- for (int i = 0; i < primitiveIds.length; i++) {
- supported[i] = vibratorInfo == null
- ? false : vibratorInfo.isPrimitiveSupported(primitiveIds[i]);
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check supported primitives; no vibrator manager.");
+ Arrays.fill(supported, false);
+ return supported;
+ }
+ int[] vibratorIds = mVibratorManager.getVibratorIds();
+ if (vibratorIds.length == 0) {
+ Arrays.fill(supported, false);
+ return supported;
+ }
+ boolean[][] vibratorSupportMap = new boolean[vibratorIds.length][primitiveIds.length];
+ for (int i = 0; i < vibratorIds.length; i++) {
+ vibratorSupportMap[i] = mVibratorManager.getVibrator(
+ vibratorIds[i]).arePrimitivesSupported(primitiveIds);
+ }
+ Arrays.fill(supported, true);
+ for (int primitiveIdx = 0; primitiveIdx < primitiveIds.length; primitiveIdx++) {
+ for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
+ if (!vibratorSupportMap[vibratorIdx][primitiveIdx]) {
+ supported[primitiveIdx] = false;
+ break;
+ }
+ }
}
return supported;
}
@Override
public void cancel() {
- if (mService == null) {
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to cancel vibrate; no vibrator manager.");
return;
}
- try {
- mService.cancelVibrate(mToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to cancel vibration.", e);
+ mVibratorManager.cancel();
+ }
+
+ /**
+ * Tries to unregister individual {@link android.os.Vibrator.OnVibratorStateChangedListener}
+ * that were left registered to vibrators after failures to register them to all vibrators.
+ *
+ * <p>This might happen if {@link AllVibratorsStateListener} fails to register to any vibrator
+ * and also fails to unregister any previously registered single listeners to other vibrators.
+ *
+ * <p>This method never throws {@link RuntimeException} if it fails to unregister again, it will
+ * fail silently and attempt to unregister the same broken listener later.
+ */
+ private void tryUnregisterBrokenListeners() {
+ synchronized (mBrokenListeners) {
+ try {
+ for (int i = mBrokenListeners.size(); --i >= 0; ) {
+ mBrokenListeners.get(i).unregister(mVibratorManager);
+ mBrokenListeners.remove(i);
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failed to unregister broken listener", e);
+ }
}
}
- @Nullable
- private VibratorInfo getVibratorInfo() {
- try {
+ /** Listener for a single vibrator state change. */
+ private static class SingleVibratorStateListener implements OnVibratorStateChangedListener {
+ private final AllVibratorsStateListener mAllVibratorsListener;
+ private final int mVibratorIdx;
+
+ SingleVibratorStateListener(AllVibratorsStateListener listener, int vibratorIdx) {
+ mAllVibratorsListener = listener;
+ mVibratorIdx = vibratorIdx;
+ }
+
+ @Override
+ public void onVibratorStateChanged(boolean isVibrating) {
+ mAllVibratorsListener.onVibrating(mVibratorIdx, isVibrating);
+ }
+ }
+
+ /** Listener for all vibrators state change. */
+ private static class AllVibratorsStateListener {
+ private final Object mLock = new Object();
+ private final Executor mExecutor;
+ private final OnVibratorStateChangedListener mDelegate;
+
+ @GuardedBy("mLock")
+ private final SparseArray<SingleVibratorStateListener> mVibratorListeners =
+ new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private int mInitializedMask;
+ @GuardedBy("mLock")
+ private int mVibratingMask;
+
+ AllVibratorsStateListener(@NonNull Executor executor,
+ @NonNull OnVibratorStateChangedListener listener) {
+ mExecutor = executor;
+ mDelegate = listener;
+ }
+
+ boolean hasRegisteredListeners() {
+ synchronized (mLock) {
+ return mVibratorListeners.size() > 0;
+ }
+ }
+
+ void register(VibratorManager vibratorManager) {
+ int[] vibratorIds = vibratorManager.getVibratorIds();
synchronized (mLock) {
- if (mVibratorInfo != null) {
- return mVibratorInfo;
+ for (int i = 0; i < vibratorIds.length; i++) {
+ int vibratorId = vibratorIds[i];
+ SingleVibratorStateListener listener = new SingleVibratorStateListener(this, i);
+ try {
+ vibratorManager.getVibrator(vibratorId).addVibratorStateListener(mExecutor,
+ listener);
+ mVibratorListeners.put(vibratorId, listener);
+ } catch (RuntimeException e) {
+ try {
+ unregister(vibratorManager);
+ } catch (RuntimeException e1) {
+ Log.w(TAG,
+ "Failed to unregister listener while recovering from a failed "
+ + "register call", e1);
+ }
+ throw e;
+ }
}
- if (mService == null) {
- return null;
+ }
+ }
+
+ void unregister(VibratorManager vibratorManager) {
+ synchronized (mLock) {
+ for (int i = mVibratorListeners.size(); --i >= 0; ) {
+ int vibratorId = mVibratorListeners.keyAt(i);
+ SingleVibratorStateListener listener = mVibratorListeners.valueAt(i);
+ vibratorManager.getVibrator(vibratorId).removeVibratorStateListener(listener);
+ mVibratorListeners.removeAt(i);
}
- return mVibratorInfo = mService.getVibratorInfo();
}
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to query vibrator info");
- throw e.rethrowFromSystemServer();
+ }
+
+ void onVibrating(int vibratorIdx, boolean vibrating) {
+ mExecutor.execute(() -> {
+ boolean anyVibrating;
+ synchronized (mLock) {
+ int allInitializedMask = 1 << mVibratorListeners.size() - 1;
+ int vibratorMask = 1 << vibratorIdx;
+ if ((mInitializedMask & vibratorMask) == 0) {
+ // First state report for this vibrator, set vibrating initial value.
+ mInitializedMask |= vibratorMask;
+ mVibratingMask |= vibrating ? vibratorMask : 0;
+ } else {
+ // Flip vibrating value, if changed.
+ boolean prevVibrating = (mVibratingMask & vibratorMask) != 0;
+ if (prevVibrating != vibrating) {
+ mVibratingMask ^= vibratorMask;
+ }
+ }
+ if (mInitializedMask != allInitializedMask) {
+ // Wait for all vibrators initial state to be reported before delegating.
+ return;
+ }
+ anyVibrating = mVibratingMask != 0;
+ }
+ mDelegate.onVibratorStateChanged(anyVibrating);
+ });
}
}
}
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
new file mode 100644
index 000000000000..b528eb157e36
--- /dev/null
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * VibratorManager implementation that controls the system vibrators.
+ *
+ * @hide
+ */
+public class SystemVibratorManager extends VibratorManager {
+ private static final String TAG = "VibratorManager";
+
+ private final IVibratorManagerService mService;
+ private final Context mContext;
+ private final Binder mToken = new Binder();
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private int[] mVibratorIds;
+ @GuardedBy("mLock")
+ private final SparseArray<Vibrator> mVibrators = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private final ArrayMap<Vibrator.OnVibratorStateChangedListener,
+ OnVibratorStateChangedListenerDelegate> mListeners = new ArrayMap<>();
+
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ public SystemVibratorManager(Context context) {
+ super(context);
+ mContext = context;
+ mService = IVibratorManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
+ }
+
+ @NonNull
+ @Override
+ public int[] getVibratorIds() {
+ synchronized (mLock) {
+ if (mVibratorIds != null) {
+ return mVibratorIds;
+ }
+ try {
+ if (mService == null) {
+ Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager service.");
+ } else {
+ return mVibratorIds = mService.getVibratorIds();
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return new int[0];
+ }
+ }
+
+ @NonNull
+ @Override
+ public Vibrator getVibrator(int vibratorId) {
+ synchronized (mLock) {
+ Vibrator vibrator = mVibrators.get(vibratorId);
+ if (vibrator != null) {
+ return vibrator;
+ }
+ VibratorInfo info = null;
+ try {
+ if (mService == null) {
+ Log.w(TAG, "Failed to retrieve vibrator; no vibrator manager service.");
+ } else {
+ info = mService.getVibratorInfo(vibratorId);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ if (info != null) {
+ vibrator = new SingleVibrator(info);
+ mVibrators.put(vibratorId, vibrator);
+ } else {
+ vibrator = NullVibrator.getInstance();
+ }
+ return vibrator;
+ }
+ }
+
+ @NonNull
+ @Override
+ public Vibrator getDefaultVibrator() {
+ return mContext.getSystemService(Vibrator.class);
+ }
+
+ @Override
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+ @Nullable CombinedVibrationEffect effect, @Nullable VibrationAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to set always-on effect; no vibrator manager service.");
+ return false;
+ }
+ try {
+ return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, attributes);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to set always-on effect.", e);
+ }
+ return false;
+ }
+
+ @Override
+ public void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
+ String reason, @Nullable VibrationAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
+ return;
+ }
+ try {
+ mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to vibrate.", e);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mService == null) {
+ Log.w(TAG, "Failed to cancel vibration; no vibrator manager service.");
+ return;
+ }
+ try {
+ mService.cancelVibrate(mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel vibration.", e);
+ }
+ }
+
+ /** Listener for vibrations on a single vibrator. */
+ private static class OnVibratorStateChangedListenerDelegate extends
+ IVibratorStateListener.Stub {
+ private final Executor mExecutor;
+ private final Vibrator.OnVibratorStateChangedListener mListener;
+
+ OnVibratorStateChangedListenerDelegate(
+ @NonNull Vibrator.OnVibratorStateChangedListener listener,
+ @NonNull Executor executor) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onVibrating(boolean isVibrating) {
+ mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+ }
+ }
+
+ /** Controls vibrations on a single vibrator. */
+ private final class SingleVibrator extends Vibrator {
+ private final VibratorInfo mVibratorInfo;
+
+ SingleVibrator(@NonNull VibratorInfo vibratorInfo) {
+ mVibratorInfo = vibratorInfo;
+ }
+
+ @Override
+ public int getId() {
+ return mVibratorInfo.getId();
+ }
+
+ @Override
+ public boolean hasVibrator() {
+ return true;
+ }
+
+ @Override
+ public boolean hasAmplitudeControl() {
+ return mVibratorInfo.hasAmplitudeControl();
+ }
+
+ @NonNull
+ @Override
+ public int[] areEffectsSupported(@NonNull int... effectIds) {
+ int[] supported = new int[effectIds.length];
+ for (int i = 0; i < effectIds.length; i++) {
+ supported[i] = mVibratorInfo.isEffectSupported(effectIds[i]);
+ }
+ return supported;
+ }
+
+ @Override
+ public boolean[] arePrimitivesSupported(
+ @NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
+ boolean[] supported = new boolean[primitiveIds.length];
+ for (int i = 0; i < primitiveIds.length; i++) {
+ supported[i] = mVibratorInfo.isPrimitiveSupported(primitiveIds[i]);
+ }
+ return supported;
+ }
+
+ @Override
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+ @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to set always-on effect on vibrator " + mVibratorInfo.getId()
+ + "; no vibrator manager service.");
+ return false;
+ }
+ try {
+ VibrationAttributes attr = new VibrationAttributes.Builder(
+ attributes, effect).build();
+ CombinedVibrationEffect combined = CombinedVibrationEffect.startSynced()
+ .addVibrator(mVibratorInfo.getId(), effect)
+ .combine();
+ return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined, attr);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to set always-on effect on vibrator " + mVibratorInfo.getId());
+ }
+ return false;
+ }
+
+ @Override
+ public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason,
+ @NonNull VibrationAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to vibrate on vibrator " + mVibratorInfo.getId()
+ + "; no vibrator manager service.");
+ return;
+ }
+ try {
+ CombinedVibrationEffect combined = CombinedVibrationEffect.startSynced()
+ .addVibrator(mVibratorInfo.getId(), vibe)
+ .combine();
+ mService.vibrate(uid, opPkg, combined, attributes, reason, mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to vibrate.", e);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mService == null) {
+ Log.w(TAG, "Failed to cancel vibration on vibrator " + mVibratorInfo.getId()
+ + "; no vibrator manager service.");
+ return;
+ }
+ try {
+ mService.cancelVibrate(mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel vibration on vibrator " + mVibratorInfo.getId(), e);
+ }
+ }
+
+ @Override
+ public boolean isVibrating() {
+ if (mService == null) {
+ Log.w(TAG, "Failed to check status of vibrator " + mVibratorInfo.getId()
+ + "; no vibrator service.");
+ return false;
+ }
+ try {
+ return mService.isVibrating(mVibratorInfo.getId());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ @Override
+ public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ if (mContext == null) {
+ Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
+ return;
+ }
+ addVibratorStateListener(mContext.getMainExecutor(), listener);
+ }
+
+ @Override
+ public void addVibratorStateListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ Objects.requireNonNull(executor);
+ if (mService == null) {
+ Log.w(TAG,
+ "Failed to add vibrate state listener to vibrator " + mVibratorInfo.getId()
+ + "; no vibrator service.");
+ return;
+ }
+ synchronized (mLock) {
+ // If listener is already registered, reject and return.
+ if (mListeners.containsKey(listener)) {
+ Log.w(TAG, "Listener already registered.");
+ return;
+ }
+ try {
+ OnVibratorStateChangedListenerDelegate delegate =
+ new OnVibratorStateChangedListenerDelegate(listener, executor);
+ if (!mService.registerVibratorStateListener(mVibratorInfo.getId(), delegate)) {
+ Log.w(TAG, "Failed to add vibrate state listener to vibrator "
+ + mVibratorInfo.getId());
+ return;
+ }
+ mListeners.put(listener, delegate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ @Override
+ public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ if (mService == null) {
+ Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
+ + mVibratorInfo.getId() + "; no vibrator service.");
+ return;
+ }
+ synchronized (mLock) {
+ // Check if the listener is registered, otherwise will return.
+ if (mListeners.containsKey(listener)) {
+ OnVibratorStateChangedListenerDelegate delegate = mListeners.get(listener);
+ try {
+ if (!mService.unregisterVibratorStateListener(mVibratorInfo.getId(),
+ delegate)) {
+ Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
+ + mVibratorInfo.getId());
+ return;
+ }
+ mListeners.remove(listener);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 7d85d13094a1..d6fa733927fb 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -184,6 +184,15 @@ public abstract class Vibrator {
}
/**
+ * Return the ID of this vibrator.
+ *
+ * @return The id of the vibrator controlled by this service.
+ */
+ public int getId() {
+ return -1;
+ }
+
+ /**
* Check whether the hardware has a vibrator.
*
* @return True if the hardware has a vibrator, else false.
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index 1d5a58745279..5dd38b6cbd86 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -17,45 +17,123 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.util.Log;
/**
- * VibratorManager provides access to multiple vibrators, as well as the ability to run them in
- * a synchronized fashion.
+ * Class that provides access to all vibrators from the device, as well as the ability to run them
+ * in a synchronized fashion.
+ * <p>
+ * If your process exits, any vibration you started will stop.
+ * </p>
*/
+@SystemService(Context.VIBRATOR_MANAGER_SERVICE)
public abstract class VibratorManager {
- /** @hide */
- protected static final String TAG = "VibratorManager";
+ private static final String TAG = "VibratorManager";
+
+ private final String mPackageName;
/**
- * {@hide}
+ * @hide to prevent subclassing from outside of the framework
*/
public VibratorManager() {
+ mPackageName = ActivityThread.currentPackageName();
+ }
+
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ protected VibratorManager(Context context) {
+ mPackageName = context.getOpPackageName();
}
/**
- * This method lists all available actuator ids, returning a possible empty list.
- * If the device has only a single actuator, this should return a single entry with a
- * default id.
+ * List all available vibrator ids, returning a possible empty list.
+ *
+ * @return An array containing the ids of the vibrators available on the device.
*/
@NonNull
public abstract int[] getVibratorIds();
/**
- * Returns a Vibrator service for given id.
- * This allows users to perform a vibration effect on a single actuator.
- */
+ * Retrieve a single vibrator by id.
+ *
+ * @param vibratorId The id of the vibrator to be retrieved.
+ * @return The vibrator with given {@code vibratorId}, never null.
+ */
@NonNull
public abstract Vibrator getVibrator(int vibratorId);
/**
- * Returns the system default Vibrator service.
- */
+ * Returns the system default Vibrator service.
+ */
@NonNull
public abstract Vibrator getDefaultVibrator();
/**
- * Vibrates all actuators by passing each VibrationEffect within CombinedVibrationEffect
- * to the respective actuator, in sync.
+ * Configure an always-on haptics effect.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+ @Nullable CombinedVibrationEffect effect, @Nullable VibrationAttributes attributes) {
+ Log.w(TAG, "Always-on effects aren't supported");
+ return false;
+ }
+
+ /**
+ * Vibrate with a given combination of effects.
+ *
+ * <p>
+ * Pass in a {@link CombinedVibrationEffect} representing a combination of {@link
+ * VibrationEffect} to be played on one or more vibrators.
+ * </p>
+ *
+ * @param effect an array of longs of times for which to turn the vibrator on or off.
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public final void vibrate(@NonNull CombinedVibrationEffect effect) {
+ vibrate(effect, null);
+ }
+
+ /**
+ * Vibrate with a given combination of effects.
+ *
+ * <p>
+ * Pass in a {@link CombinedVibrationEffect} representing a combination of {@link
+ * VibrationEffect} to be played on one or more vibrators.
+ * </p>
+ *
+ * @param effect an array of longs of times for which to turn the vibrator on or off.
+ * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
+ * specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with
+ * incoming calls.
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public final void vibrate(@NonNull CombinedVibrationEffect effect,
+ @Nullable VibrationAttributes attributes) {
+ vibrate(Process.myUid(), mPackageName, effect, null, attributes);
+ }
+
+ /**
+ * Like {@link #vibrate(CombinedVibrationEffect, VibrationAttributes)}, but allows the
+ * caller to specify the vibration is owned by someone else and set reason for vibration.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public abstract void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
+ String reason, @Nullable VibrationAttributes attributes);
+
+ /**
+ * Turn all the vibrators off.
*/
- public abstract void vibrate(@NonNull CombinedVibrationEffect effect);
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public abstract void cancel();
}
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index cbc0479a4c07..20f05981e962 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -32,7 +32,6 @@ public final class SurfaceSession {
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
- private static native void nativeKill(long ptr);
/** Create a new connection with the surface flinger. */
@UnsupportedAppUsage
@@ -44,22 +43,22 @@ public final class SurfaceSession {
@Override
protected void finalize() throws Throwable {
try {
- if (mNativeClient != 0) {
- nativeDestroy(mNativeClient);
- }
+ kill();
} finally {
super.finalize();
}
}
/**
- * Forcibly detach native resources associated with this object.
- * Unlike destroy(), after this call any surfaces that were created
- * from the session will no longer work.
+ * Remove the reference to the native Session object. The native object may still exist if
+ * there are other references to it, but it cannot be accessed from this Java object anymore.
*/
@UnsupportedAppUsage
public void kill() {
- nativeKill(mNativeClient);
+ if (mNativeClient != 0) {
+ nativeDestroy(mNativeClient);
+ mNativeClient = 0;
+ }
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 70ec2d42b59b..6eba83fee48c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1083,7 +1083,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (creating) {
updateOpaqueFlag();
- mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot);
+ final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
+ if (mUseBlastAdapter) {
+ createBlastSurfaceControls(viewRoot, name);
+ } else {
+ mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot, name);
+ }
} else if (mSurfaceControl == null) {
return;
}
@@ -1220,53 +1225,77 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* out, the old surface can be persevered until the new one has drawn by keeping the reference
* of the old SurfaceControl alive.
*/
- private SurfaceControl createSurfaceControls(ViewRootImpl viewRoot) {
- final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
-
- SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession)
+ private SurfaceControl createSurfaceControls(ViewRootImpl viewRoot, String name) {
+ final SurfaceControl previousSurfaceControl = mSurfaceControl;
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name)
.setLocalOwnerView(this)
.setParent(viewRoot.getBoundsLayer())
- .setCallsite("SurfaceView.updateSurface");
+ .setCallsite("SurfaceView.updateSurface")
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFlags(mSurfaceFlags)
+ .setFormat(mFormat)
+ .build();
+ mBackgroundControl = createBackgroundControl(name);
+ return previousSurfaceControl;
+ }
- final SurfaceControl previousSurfaceControl;
- if (mUseBlastAdapter) {
- mSurfaceControl = builder
+ private SurfaceControl createBackgroundControl(String name) {
+ return new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Background for " + name)
+ .setLocalOwnerView(this)
+ .setOpaque(true)
+ .setColorLayer()
+ .setParent(mSurfaceControl)
+ .setCallsite("SurfaceView.updateSurface")
+ .build();
+ }
+
+ // We don't recreate the surface controls but only recreate the adapter. Since the blast layer
+ // is still alive, the old buffers will continue to be presented until replaced by buffers from
+ // the new adapter. This means we do not need to track the old surface control and destroy it
+ // after the client has drawn to avoid any flickers.
+ private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name) {
+ if (mSurfaceControl == null) {
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name)
+ .setLocalOwnerView(this)
+ .setParent(viewRoot.getBoundsLayer())
+ .setCallsite("SurfaceView.updateSurface")
.setContainerLayer()
.build();
- previousSurfaceControl = mBlastSurfaceControl;
+ }
+
+ if (mBlastSurfaceControl == null) {
mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name + "(BLAST)")
.setLocalOwnerView(this)
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setParent(mSurfaceControl)
.setFlags(mSurfaceFlags)
.setHidden(false)
.setBLASTLayer()
.setCallsite("SurfaceView.updateSurface")
.build();
- mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
- mSurfaceHeight, mFormat, true /* TODO */);
} else {
- previousSurfaceControl = mSurfaceControl;
- mSurfaceControl = builder
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
- .setFlags(mSurfaceFlags)
- .setFormat(mFormat)
- .build();
- mBlastSurfaceControl = null;
- mBlastBufferQueue = null;
+ // update blast layer
+ mTmpTransaction
+ .setOpaque(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
+ .setSecure(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.SECURE) != 0)
+ .show(mBlastSurfaceControl)
+ .apply();
}
- mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
- .setName("Background for " + name)
- .setLocalOwnerView(this)
- .setOpaque(true)
- .setColorLayer()
- .setParent(mSurfaceControl)
- .setCallsite("SurfaceView.updateSurface")
- .build();
- return previousSurfaceControl;
+ if (mBackgroundControl == null) {
+ mBackgroundControl = createBackgroundControl(name);
+ }
+
+ // Always recreate the IGBP for compatibility. This can be optimized in the future but
+ // the behavior change will need to be gated by SDK version.
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ }
+ mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight, mFormat, true /* TODO */);
}
private void onDrawFinished() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4716141ee8d3..6b13a290b20e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1898,11 +1898,12 @@ public final class ViewRootImpl implements ViewParent,
}
private void setBoundsLayerCrop(Transaction t) {
- // mWinFrame is already adjusted for surface insets. So offset it and use it as
- // the cropping bounds.
- mTempBoundsRect.set(mWinFrame);
- mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
- mWindowAttributes.surfaceInsets.top);
+ // Adjust of insets and update the bounds layer so child surfaces do not draw into
+ // the surface inset region.
+ mTempBoundsRect.set(0, 0, mSurfaceSize.x, mSurfaceSize.y);
+ mTempBoundsRect.inset(mWindowAttributes.surfaceInsets.left,
+ mWindowAttributes.surfaceInsets.top,
+ mWindowAttributes.surfaceInsets.right, mWindowAttributes.surfaceInsets.bottom);
t.setWindowCrop(mBoundsLayer, mTempBoundsRect);
}
@@ -1913,25 +1914,18 @@ public final class ViewRootImpl implements ViewParent,
private boolean updateBoundsLayer(SurfaceControl.Transaction t) {
if (mBoundsLayer != null) {
setBoundsLayerCrop(t);
- t.deferTransactionUntil(mBoundsLayer, getSurfaceControl(),
- mSurface.getNextFrameNumber());
return true;
}
return false;
}
- private void prepareSurfaces(boolean sizeChanged) {
+ private void prepareSurfaces() {
final SurfaceControl.Transaction t = mTransaction;
final SurfaceControl sc = getSurfaceControl();
if (!sc.isValid()) return;
- boolean applyTransaction = updateBoundsLayer(t);
- if (sizeChanged) {
- applyTransaction = true;
- t.setBufferSize(sc, mSurfaceSize.x, mSurfaceSize.y);
- }
- if (applyTransaction) {
- t.apply();
+ if (updateBoundsLayer(t)) {
+ mergeWithNextTransaction(t, mSurface.getNextFrameNumber());
}
}
@@ -3036,7 +3030,7 @@ public final class ViewRootImpl implements ViewParent,
// stopping, but on the client side it doesn't get stopped since it's restarted quick
// enough. WMS doesn't want to keep around old children since they will leak when the
// client creates new children.
- prepareSurfaces(surfaceSizeChanged);
+ prepareSurfaces();
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -3070,8 +3064,10 @@ public final class ViewRootImpl implements ViewParent,
// via the WM relayout code path. We probably eventually
// want to synchronize transparent region hint changes
// with draws.
- mTransaction.setTransparentRegionHint(getSurfaceControl(),
- mTransparentRegion).apply();
+ SurfaceControl sc = getSurfaceControl();
+ if (sc.isValid()) {
+ mTransaction.setTransparentRegionHint(sc, mTransparentRegion).apply();
+ }
}
}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 61ff36c09cb9..681fd147a51f 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -18,6 +18,7 @@ package android.widget;
import android.annotation.ColorInt;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -27,6 +28,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
+import android.util.AttributeSet;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -111,11 +113,14 @@ public class EdgeEffect {
private float mGlowAlpha;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mGlowScaleY;
+ private float mDistance;
private float mGlowAlphaStart;
private float mGlowAlphaFinish;
private float mGlowScaleYStart;
private float mGlowScaleYFinish;
+ private float mDistanceStart;
+ private float mDistanceFinish;
private long mStartTime;
private float mDuration;
@@ -150,9 +155,18 @@ public class EdgeEffect {
* @param context Context used to provide theming and resource information for the EdgeEffect
*/
public EdgeEffect(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Construct a new EdgeEffect with a theme appropriate for the provided context.
+ * @param context Context used to provide theming and resource information for the EdgeEffect
+ * @param attrs The attributes of the XML tag that is inflating the view
+ */
+ public EdgeEffect(@NonNull Context context, @Nullable AttributeSet attrs) {
mPaint.setAntiAlias(true);
final TypedArray a = context.obtainStyledAttributes(
- com.android.internal.R.styleable.EdgeEffect);
+ attrs, com.android.internal.R.styleable.EdgeEffect);
final int themeColor = a.getColor(
com.android.internal.R.styleable.EdgeEffect_colorEdgeEffect, 0xff666666);
mEdgeEffectType = a.getInt(
@@ -248,6 +262,7 @@ public class EdgeEffect {
mDuration = PULL_TIME;
mPullDistance += deltaDistance;
+ mDistanceStart = mDistanceFinish = mDistance = Math.max(0f, mPullDistance);
final float absdd = Math.abs(deltaDistance);
mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
@@ -267,6 +282,56 @@ public class EdgeEffect {
}
/**
+ * A view should call this when content is pulled away from an edge by the user.
+ * This will update the state of the current visual effect and its associated animation.
+ * The host view should always {@link android.view.View#invalidate()} after this
+ * and draw the results accordingly. This works similarly to {@link #onPull(float, float)},
+ * but returns the amount of <code>deltaDistance</code> that has been consumed. If the
+ * {@link #getDistance()} is currently 0 and <code>deltaDistance</code> is negative, this
+ * function will return 0 and the drawn value will remain unchanged.
+ *
+ * This method can be used to reverse the effect from a pull or absorb and partially consume
+ * some of a motion:
+ *
+ * <pre class="prettyprint">
+ * if (deltaY < 0) {
+ * float consumed = edgeEffect.onPullDistance(deltaY / getHeight(), x / getWidth());
+ * deltaY -= consumed * getHeight();
+ * if (edgeEffect.getDistance() == 0f) edgeEffect.onRelease();
+ * }
+ * </pre>
+ *
+ * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+ * 1.f (full length of the view) or negative values to express change
+ * back toward the edge reached to initiate the effect.
+ * @param displacement The displacement from the starting side of the effect of the point
+ * initiating the pull. In the case of touch this is the finger position.
+ * Values may be from 0-1.
+ * @return The amount of <code>deltaDistance</code> that was consumed, a number between
+ * 0 and <code>deltaDistance</code>.
+ */
+ public float onPullDistance(float deltaDistance, float displacement) {
+ float finalDistance = Math.max(0f, deltaDistance + mDistance);
+ float delta = finalDistance - mDistance;
+ onPull(delta, displacement);
+ return delta;
+ }
+
+ /**
+ * Returns the pull distance needed to be released to remove the showing effect.
+ * It is determined by the {@link #onPull(float, float)} <code>deltaDistance</code> and
+ * any animating values, including from {@link #onAbsorb(int)} and {@link #onRelease()}.
+ *
+ * This can be used in conjunction with {@link #onPullDistance(float, float)} to
+ * release the currently showing effect.
+ *
+ * @return The pull distance that must be released to remove the showing effect.
+ */
+ public float getDistance() {
+ return mDistance;
+ }
+
+ /**
* Call when the object is released after being pulled.
* This will begin the "decay" phase of the effect. After calling this method
* the host view should {@link android.view.View#invalidate()} and thereby
@@ -282,9 +347,11 @@ public class EdgeEffect {
mState = STATE_RECEDE;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
+ mDistanceStart = mDistance;
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
+ mDistanceFinish = 0.f;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
@@ -311,7 +378,7 @@ public class EdgeEffect {
// nearly invisible.
mGlowAlphaStart = GLOW_ALPHA_START;
mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
-
+ mDistanceStart = mDistance;
// Growth for the size of the glow should be quadratic to properly
// respond
@@ -322,6 +389,9 @@ public class EdgeEffect {
mGlowAlphaFinish = Math.max(
mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
mTargetDisplacement = 0.5f;
+
+ // Use glow values to estimate the absorption for stretch distance.
+ mDistanceFinish = calculateDistanceFromGlowValues(mGlowScaleYFinish, mGlowAlphaFinish);
}
/**
@@ -447,6 +517,7 @@ public class EdgeEffect {
mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
+ mDistance = mDistanceStart + (mDistanceFinish - mDistanceStart) * interp;
mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
if (t >= 1.f - EPSILON) {
@@ -458,10 +529,12 @@ public class EdgeEffect {
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
+ mDistanceStart = mDistance;
// After absorb, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
+ mDistanceFinish = 0.f;
break;
case STATE_PULL:
mState = STATE_PULL_DECAY;
@@ -470,10 +543,12 @@ public class EdgeEffect {
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
+ mDistanceStart = mDistance;
// After pull, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
+ mDistanceFinish = 0.f;
break;
case STATE_PULL_DECAY:
mState = STATE_RECEDE;
@@ -484,4 +559,20 @@ public class EdgeEffect {
}
}
}
+
+ /**
+ * @return The estimated pull distance as calculated from mGlowScaleY.
+ */
+ private float calculateDistanceFromGlowValues(float scale, float alpha) {
+ if (scale >= 1f) {
+ // It should asymptotically approach 1, but not reach there.
+ // Here, we're just choosing a value that is large.
+ return 1f;
+ }
+ if (scale > 0f) {
+ float v = 1f / 0.7f / (mGlowScaleY - 1f);
+ return v * v / mBounds.height();
+ }
+ return alpha / PULL_DISTANCE_ALPHA_GLOW_FACTOR;
+ }
}
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 9049ca56bc53..fae58622d91e 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal;
+package com.android.internal.display;
import android.content.ContentResolver;
diff --git a/core/java/com/android/internal/display/OWNERS b/core/java/com/android/internal/display/OWNERS
new file mode 100644
index 000000000000..20b75be9f11f
--- /dev/null
+++ b/core/java/com/android/internal/display/OWNERS
@@ -0,0 +1,3 @@
+include /services/core/java/com/android/server/display/OWNERS
+
+flc@google.com
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6e41b3f4ae06..96a71b994375 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -7157,20 +7157,34 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public long getScreenOnEnergy() {
+ return getMeasuredEnergyMicroJoules(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ }
+
+ @Override
+ public long getScreenDozeEnergy() {
+ return getMeasuredEnergyMicroJoules(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
+ }
+
+ /**
+ * Returns the energy in microjoules that the given standard energy bucket consumed.
+ * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable
+ *
+ * @param bucket standard energy bucket of interest
+ * @return energy (in microjoules) used for this energy bucket
+ */
+ private long getMeasuredEnergyMicroJoules(@StandardEnergyBucket int bucket) {
if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE;
}
- return mGlobalMeasuredEnergyStats
- .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ return mGlobalMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
}
@Override
- public long getScreenDozeEnergy() {
+ public @Nullable long[] getCustomMeasuredEnergiesMicroJoules() {
if (mGlobalMeasuredEnergyStats == null) {
- return ENERGY_DATA_UNAVAILABLE;
+ return null;
}
- return mGlobalMeasuredEnergyStats
- .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
+ return mGlobalMeasuredEnergyStats.getAccumulatedCustomBucketEnergies();
}
@Override public long getStartClockTime() {
@@ -7941,6 +7955,13 @@ public class BatteryStatsImpl extends BatteryStats {
.updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
}
+ /** Adds the given energy to the given custom energy bucket for this uid. */
+ private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket,
+ boolean accumulate) {
+ getOrCreateMeasuredEnergyStatsLocked()
+ .updateCustomBucket(energyBucket, energyDeltaUJ, accumulate);
+ }
+
/**
* Returns the energy used by this uid for a standard energy bucket of interest.
* @param bucket standard energy bucket of interest
@@ -7957,6 +7978,18 @@ public class BatteryStatsImpl extends BatteryStats {
return mUidMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
}
+ @Override
+ public long[] getCustomMeasuredEnergiesMicroJoules() {
+ if (mBsi.mGlobalMeasuredEnergyStats == null) {
+ return null;
+ }
+ if (mUidMeasuredEnergyStats == null) {
+ // Custom buckets may exist. But all values for this uid are 0 so we report all 0s.
+ return new long[mBsi.mGlobalMeasuredEnergyStats.getNumberCustomEnergyBuckets()];
+ }
+ return mUidMeasuredEnergyStats.getAccumulatedCustomBucketEnergies();
+ }
+
/**
* Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
* since last marked. Also sets the mark time for both these timers.
@@ -12465,6 +12498,42 @@ public class BatteryStatsImpl extends BatteryStats {
}
/**
+ * Accumulate Custom energy bucket energy, globally and for each app.
+ *
+ * @param totalEnergyUJ energy (microjoules) used for this bucket since this was last called.
+ * @param uidEnergies map of uid->energy (microjoules) for this bucket since last called.
+ * Data inside uidEnergies will not be modified (treated immutable).
+ */
+ public void updateCustomMeasuredEnergyDataLocked(int customEnergyBucket,
+ long totalEnergyUJ, @Nullable SparseLongArray uidEnergies) {
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, "Updating attributed measured energy stats for custom bucket "
+ + customEnergyBucket
+ + " with total energy " + totalEnergyUJ
+ + " and uid energies " + String.valueOf(uidEnergies));
+ }
+ if (mGlobalMeasuredEnergyStats == null) return;
+ if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalEnergyUJ <= 0) return;
+
+ mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ, true);
+
+ if (uidEnergies == null) return;
+ final int numUids = uidEnergies.size();
+ for (int i = 0; i < numUids; i++) {
+ final int uidInt = mapUid(uidEnergies.keyAt(i));
+ final long uidEnergyUJ = uidEnergies.valueAt(i);
+ if (uidEnergyUJ == 0) continue;
+ // TODO: Worry about uids not in BSI currently, including uninstalled uids 'coming back'
+ // Specifically: What if the uid had been removed? We'll re-create it now.
+ // And if we instead use getAvailableUidStatsLocked() and chec for null, then we might
+ // not create a Uid even when we should be (say, the app's first event, somehow, was to
+ // use GPU). I guess that CPU/kernel data might already have this problem?
+ final Uid uidObj = getUidStatsLocked(uidInt);
+ uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true);
+ }
+ }
+
+ /**
* Read and record Rail Energy data.
*/
public void updateRailStatsLocked() {
@@ -14454,7 +14523,12 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@GuardedBy("this")
public void dumpMeasuredEnergyStatsLocked(PrintWriter pw) {
- if (mGlobalMeasuredEnergyStats == null) return;
+ pw.printf("On battery measured energy stats (microjoules) \n");
+ if (mGlobalMeasuredEnergyStats == null) {
+ pw.printf(" Not supported on this device.\n");
+ return;
+ }
+
dumpMeasuredEnergyStatsLocked(pw, "non-uid usage", mGlobalMeasuredEnergyStats);
int size = mUidStats.size();
@@ -14472,7 +14546,8 @@ public class BatteryStatsImpl extends BatteryStats {
MeasuredEnergyStats stats) {
if (stats == null) return;
final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ");
- iPw.printf("On battery measured energy stats for %s:\n", name);
+ iPw.increaseIndent();
+ iPw.printf("%s:\n", name);
iPw.increaseIndent();
stats.dump(iPw);
iPw.decreaseIndent();
diff --git a/core/java/com/android/internal/power/MeasuredEnergyArray.java b/core/java/com/android/internal/power/MeasuredEnergyArray.java
deleted file mode 100644
index 1f6dc260a197..000000000000
--- a/core/java/com/android/internal/power/MeasuredEnergyArray.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.power;
-
-
-import android.annotation.IntDef;
-
-import com.android.internal.os.RailStats;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Interface to provide subsystem energy data.
- * TODO: replace this and {@link RailStats} once b/173077356 is done
- */
-public interface MeasuredEnergyArray {
- int SUBSYSTEM_UNKNOWN = -1;
- int SUBSYSTEM_DISPLAY = 0;
- int NUMBER_SUBSYSTEMS = 1;
- String[] SUBSYSTEM_NAMES = {"display"};
-
-
- @IntDef(prefix = { "SUBSYSTEM_" }, value = {
- SUBSYSTEM_UNKNOWN,
- SUBSYSTEM_DISPLAY,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface MeasuredEnergySubsystem {}
-
- /**
- * Get the subsystem at an index in array.
- *
- * @param index into the array.
- * @return subsystem.
- */
- @MeasuredEnergySubsystem
- int getSubsystem(int index);
-
- /**
- * Get the energy (in microjoules) consumed since boot of the subsystem at an index.
- *
- * @param index into the array.
- * @return energy (in microjoules) consumed since boot.
- */
- long getEnergy(int index);
-
- /**
- * Return number of subsystems in the array.
- */
- int size();
-}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index e310f8d9b36e..d7b4d78c56cf 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -28,7 +28,6 @@ import android.util.Slog;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -239,6 +238,7 @@ public class MeasuredEnergyStats {
* Return accumulated energy (in microjoules) for the a custom energy bucket since last reset.
* Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
*/
+ @VisibleForTesting
public long getAccumulatedCustomBucketEnergy(int customBucket) {
if (!isValidCustomBucket(customBucket)) {
return ENERGY_DATA_UNAVAILABLE;
@@ -247,7 +247,18 @@ public class MeasuredEnergyStats {
}
/**
- * Map {@link MeasuredEnergySubsystem} and device state to Display {@link StandardEnergyBucket}.
+ * Return accumulated energies (in microjoules) for all custom energy buckets since last reset.
+ */
+ public @NonNull long[] getAccumulatedCustomBucketEnergies() {
+ final long[] energies = new long[getNumberCustomEnergyBuckets()];
+ for (int bucket = 0; bucket < energies.length; bucket++) {
+ energies[bucket] = mAccumulatedEnergiesMicroJoules[customBucketToIndex(bucket)];
+ }
+ return energies;
+ }
+
+ /**
+ * Map {@link android.view.Display} STATE_ to corresponding {@link StandardEnergyBucket}.
*/
public static @StandardEnergyBucket int getDisplayEnergyBucket(int screenState) {
if (Display.isOnState(screenState)) {
@@ -405,7 +416,6 @@ public class MeasuredEnergyStats {
/** Dump debug data. */
public void dump(PrintWriter pw) {
- pw.println("Accumulated energy since last reset (microjoules):");
pw.print(" ");
for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
pw.print(getBucketName(index));
@@ -432,6 +442,11 @@ public class MeasuredEnergyStats {
return "CUSTOM_" + indexToCustomBucket(index);
}
+ /** Get the number of custom energy buckets on this device. */
+ public int getNumberCustomEnergyBuckets() {
+ return mAccumulatedEnergiesMicroJoules.length - NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
private static int customBucketToIndex(int customBucket) {
return customBucket + NUMBER_STANDARD_ENERGY_BUCKETS;
}
@@ -450,7 +465,9 @@ public class MeasuredEnergyStats {
return bucket >= 0 && bucket < NUMBER_STANDARD_ENERGY_BUCKETS;
}
- private boolean isValidCustomBucket(int customBucket) {
+ /** Returns whether the given custom bucket is valid (exists) on this device. */
+ @VisibleForTesting
+ public boolean isValidCustomBucket(int customBucket) {
return customBucket >= 0
&& customBucketToIndex(customBucket) < mAccumulatedEnergiesMicroJoules.length;
}
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 191f748d80bf..0aac07d17cdc 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -51,19 +51,12 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
client->decStrong((void*)nativeCreate);
}
-static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) {
- SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
- client->dispose();
-}
-
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeCreate", "()J",
(void*)nativeCreate },
{ "nativeDestroy", "(J)V",
(void*)nativeDestroy },
- { "nativeKill", "(J)V",
- (void*)nativeKill }
};
int register_android_view_SurfaceSession(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dc1cc329d460..99014c5442b0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1055,6 +1055,14 @@
android:description="@string/permdesc_accessImsCallService"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows an application to perform IMS Single Registration related actions.
+ Only granted if the application is a system app AND is in the Default SMS Role.
+ The permission is revoked when the app is taken out of the Default SMS Role.
+ <p>Protection level: internal|role
+ -->
+ <permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to read the user's call log.
<p class="note"><strong>Note:</strong> If your app uses the
{@link #READ_CONTACTS} permission and <em>both</em> your <a
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b4e580aac959..0ae6a76e2a60 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -311,6 +311,10 @@
<flag name="recents" value="0x2000000" />
<!-- Additional flag from base permission type: this permission is managed by role. -->
<flag name="role" value="0x4000000" />
+ <!-- Additional flag from base permission type: this permission can also be granted if the
+ requesting application is signed by, or has in its signing lineage, any of the
+ certificate digests declared in {@link android.R.attr#knownCerts}. -->
+ <flag name="knownSigner" value="0x8000000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
@@ -364,6 +368,15 @@
{@link android.R.styleable#AndroidManifestPermissionGroup permission-group} tag. -->
<attr name="permissionGroup" format="string" />
+ <!-- A reference to an array resource containing the signing certificate digests to be granted
+ this permission when using the {@code knownSigner} protection flag. The digest should
+ be computed over the DER encoding of the trusted certificate using the SHA-256 digest
+ algorithm.
+ <p>
+ If only a single signer is declared this can also be a string resource, or the digest
+ can be declared inline as the value for this attribute. -->
+ <attr name="knownCerts" format="reference|string" />
+
<!-- Specify the name of a user ID that will be shared between multiple
packages. By default, each package gets its own unique user-id.
By setting this value on two or more packages, each of these packages
@@ -1935,6 +1948,7 @@
<attr name="request" />
<attr name="protectionLevel" />
<attr name="permissionFlags" />
+ <attr name="knownCerts" />
</declare-styleable>
<!-- The <code>permission-group</code> tag declares a logical grouping of
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 46f928df323c..068987ec8afe 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3064,6 +3064,7 @@
<public name="previewLayout" />
<public name="clipToOutline" />
<public name="edgeEffectType" />
+ <public name="knownCerts" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/tests/coretests/src/android/app/timedetector/ExternalTimeSuggestionTest.java b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
index 6bcec25b9368..266ff3dd09f6 100644
--- a/core/tests/coretests/src/android/app/timedetector/ExternalTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.timedetector;
+package android.app.time;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index bffd1e4a86d6..49b720cfba07 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.fail;
import android.content.pm.PackageParser.SigningDetails;
import android.util.ArraySet;
+import android.util.PackageUtils;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -817,6 +818,124 @@ public class SigningDetailsTest {
assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
}
+ @Test
+ public void hasAncestorOrSelfWithDigest_nullSet_returnsFalse() throws Exception {
+ // The hasAncestorOrSelfWithDigest method is intended to verify whether the SigningDetails
+ // is currently signed, or has previously been signed, by any of the certificate digests
+ // in the provided Set. This test verifies if a null Set is provided then false is returned.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(null));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_unknownDetails_returnsFalse() throws Exception {
+ // If hasAncestorOrSelfWithDigest is invoked against an UNKNOWN
+ // instance of the SigningDetails then false is returned.
+ SigningDetails details = SigningDetails.UNKNOWN;
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_singleSignerInSet_returnsTrue() throws Exception {
+ // If the single signer of an app is in the provided digest Set then
+ // the method should return true.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, SECOND_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_singleSignerNotInSet_returnsFalse() throws Exception {
+ // If the single signer of an app is not in the provided digest Set then
+ // the method should return false.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE);
+ Set<String> digests = createDigestSet(SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_multipleSignersInSet_returnsTrue() throws Exception {
+ // If an app is signed by multiple signers and all of the signers are in
+ // the digest Set then the method should return true.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_multipleSignersNotInSet_returnsFalse()
+ throws Exception {
+ // If an app is signed by multiple signers then all signers must be in the digest Set; if
+ // only a subset of the signers are in the Set then the method should return false.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, THIRD_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_multipleSignersOneInSet_returnsFalse()
+ throws Exception {
+ // If an app is signed by multiple signers and the Set size is smaller than the number of
+ // signers then the method should immediately return false since there's no way for the
+ // requirement of all signers in the Set to be met.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_lineageSignerInSet_returnsTrue() throws Exception {
+ // If an app has a rotated signing key and a previous key in the lineage is in the digest
+ // Set then this method should return true.
+ SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, THIRD_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_lineageSignerNotInSet_returnsFalse() throws Exception {
+ // If an app has a rotated signing key, but neither the current key nor any of the signers
+ // in the lineage are in the digest set then the method should return false.
+ SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(THIRD_SIGNATURE, FOURTH_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_lastSignerInLineageInSet_returnsTrue()
+ throws Exception {
+ // If an app has multiple signers in the lineage only one of those signers must be in the
+ // Set for this method to return true. This test verifies if the last signer in the lineage
+ // is in the set then the method returns true.
+ SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE,
+ THIRD_SIGNATURE);
+ Set<String> digests = createDigestSet(SECOND_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_nullLineageSingleSIgner_returnsFalse()
+ throws Exception {
+ // Under some instances an app with only a single signer can have a null lineage; this
+ // test verifies that null lineage does not result in a NullPointerException and instead the
+ // method returns false if the single signer is not in the Set.
+ SigningDetails details = createSigningDetails(true, FIRST_SIGNATURE);
+ Set<String> digests = createDigestSet(SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception {
int[] capabilities = new int[signers.length];
for (int i = 0; i < capabilities.length; i++) {
@@ -853,12 +972,21 @@ public class SigningDetailsTest {
// If there are multiple signers then the pastSigningCertificates should be set to null, but
// if there is only a single signer both the current signer and the past signers should be
// set to that one signer.
- if (signers.length > 1) {
+ if (signers.length > 1 || useNullPastSigners) {
return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null);
}
return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures);
}
+ private Set<String> createDigestSet(String... signers) {
+ Set<String> digests = new ArraySet<>();
+ for (String signer : signers) {
+ String digest = PackageUtils.computeSha256Digest(new Signature(signer).toByteArray());
+ digests.add(digest);
+ }
+ return digests;
+ }
+
private void assertSigningDetailsContainsLineage(SigningDetails details,
String... pastSigners) {
// This method should only be invoked for results that contain a single signer.
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
index 564103efef65..11239db9f404 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -17,6 +17,8 @@
package android.os;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
@@ -31,7 +33,6 @@ import java.util.Arrays;
@Presubmit
@RunWith(JUnit4.class)
public class CombinedVibrationEffectTest {
-
private static final VibrationEffect VALID_EFFECT = VibrationEffect.createOneShot(10, 255);
private static final VibrationEffect INVALID_EFFECT = new VibrationEffect.OneShot(-1, -1);
@@ -172,6 +173,37 @@ public class CombinedVibrationEffectTest {
}
@Test
+ public void testHasVibratorMono_returnsTrueForAnyVibrator() {
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ assertTrue(effect.hasVibrator(0));
+ assertTrue(effect.hasVibrator(1));
+ }
+
+ @Test
+ public void testHasVibratorStereo_returnsOnlyTheIdsSet() {
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .combine();
+ assertFalse(effect.hasVibrator(0));
+ assertTrue(effect.hasVibrator(1));
+ assertFalse(effect.hasVibrator(2));
+ }
+
+ @Test
+ public void testHasVibratorSequential_returnsNestedVibrators() {
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(CombinedVibrationEffect.startSynced()
+ .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .combine())
+ .combine();
+ assertFalse(effect.hasVibrator(0));
+ assertTrue(effect.hasVibrator(1));
+ assertTrue(effect.hasVibrator(2));
+ }
+
+ @Test
public void testSerializationMono() {
CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(VALID_EFFECT);
diff --git a/core/tests/coretests/src/android/provider/OWNERS b/core/tests/coretests/src/android/provider/OWNERS
new file mode 100644
index 000000000000..581da714f326
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/provider/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 97c07eaa819b..6652c64c4344 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -24,6 +24,7 @@ import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.Uid.Sensor;
import android.os.WorkSource;
+import android.util.SparseLongArray;
import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -583,6 +584,95 @@ public class BatteryStatsNoteTest extends TestCase {
checkMeasuredEnergy("H", uid1, blame1, uid2, blame2, globalDoze, bi);
}
+ @SmallTest
+ public void testUpdateCustomMeasuredEnergyDataLocked_neverCalled() {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.setOnBatteryInternal(true);
+
+ final int uid1 = 11500;
+ final int uid2 = 11501;
+
+ // Initially, all custom buckets report energy of 0.
+ checkCustomMeasuredEnergy("0", 0, 0, uid1, 0, 0, uid2, 0, 0, bi);
+ }
+
+ @SmallTest
+ public void testUpdateCustomMeasuredEnergyDataLocked() {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ final int bucketA = 0; // Custom bucket 0
+ final int bucketB = 1; // Custom bucket 1
+
+ long totalBlameA = 0; // Total energy consumption for bucketA (may exceed sum of uids)
+ long totalBlameB = 0; // Total energy consumption for bucketB (may exceed sum of uids)
+
+ final int uid1 = 10500;
+ long blame1A = 0; // Blame for uid1 in bucketA
+ long blame1B = 0; // Blame for uid1 in bucketB
+
+ final int uid2 = 10501;
+ long blame2A = 0; // Blame for uid2 in bucketA
+ long blame2B = 0; // Blame for uid2 in bucketB
+
+ final SparseLongArray newEnergiesA = new SparseLongArray(2);
+ final SparseLongArray newEnergiesB = new SparseLongArray(2);
+
+
+ // ----- Case A: battery off (so blame does not increase)
+ bi.setOnBatteryInternal(false);
+
+ newEnergiesA.put(uid1, 20_000);
+ // Implicit newEnergiesA.put(uid2, 0);
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 500_000, newEnergiesA);
+
+ newEnergiesB.put(uid1, 60_000);
+ // Implicit newEnergiesB.put(uid2, 0);
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 700_000, newEnergiesB);
+
+ checkCustomMeasuredEnergy(
+ "A", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+
+
+ // ----- Case B: battery on
+ bi.setOnBatteryInternal(true);
+
+ newEnergiesA.put(uid1, 7_000); blame1A += 7_000;
+ // Implicit newEnergiesA.put(uid2, 0); blame2A += 0;
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 310_000, newEnergiesA);
+ totalBlameA += 310_000;
+
+ newEnergiesB.put(uid1, 63_000); blame1B += 63_000;
+ newEnergiesB.put(uid2, 15_000); blame2B += 15_000;
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 790_000, newEnergiesB);
+ totalBlameB += 790_000;
+
+ checkCustomMeasuredEnergy(
+ "B", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+
+
+ // ----- Case C: battery still on
+ newEnergiesA.delete(uid1); blame1A += 0;
+ newEnergiesA.put(uid2, 16_000); blame2A += 16_000;
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 560_000, newEnergiesA);
+ totalBlameA += 560_000;
+
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 10_000, null);
+ totalBlameB += 10_000;
+
+ checkCustomMeasuredEnergy(
+ "C", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+
+
+ // ----- Case D: battery still on
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 0, newEnergiesA);
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 15_000, new SparseLongArray(1));
+ totalBlameB += 15_000;
+ checkCustomMeasuredEnergy(
+ "D", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+ }
+
private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
// Note that noteUidProcessStateLocked uses ActivityManager process states.
if (fgOn) {
@@ -610,4 +700,33 @@ public class BatteryStatsNoteTest extends TestCase {
assertEquals("Wrong doze for Case " + caseName, globalDoze,
bi.getScreenDozeEnergy());
}
+
+ private void checkCustomMeasuredEnergy(String caseName,
+ long totalBlameA, long totalBlameB,
+ int uid1, long blame1A, long blame1B,
+ int uid2, long blame2A, long blame2B,
+ MockBatteryStatsImpl bi) {
+
+ final long[] actualTotal = bi.getCustomMeasuredEnergiesMicroJoules();
+ final long[] actualUid1 = bi.getUidStatsLocked(uid1).getCustomMeasuredEnergiesMicroJoules();
+ final long[] actualUid2 = bi.getUidStatsLocked(uid2).getCustomMeasuredEnergiesMicroJoules();
+
+ assertNotNull(actualTotal);
+ assertNotNull(actualUid1);
+ assertNotNull(actualUid2);
+
+ assertEquals("Wrong total blame in bucket 0 for Case " + caseName, totalBlameA,
+ actualTotal[0]);
+
+ assertEquals("Wrong total blame in bucket 1 for Case " + caseName, totalBlameB,
+ actualTotal[1]);
+
+ assertEquals("Wrong uid1 blame in bucket 0 for Case " + caseName, blame1A, actualUid1[0]);
+
+ assertEquals("Wrong uid1 blame in bucket 1 for Case " + caseName, blame1B, actualUid1[1]);
+
+ assertEquals("Wrong uid2 blame in bucket 0 for Case " + caseName, blame2A, actualUid2[0]);
+
+ assertEquals("Wrong uid2 blame in bucket 1 for Case " + caseName, blame2B, actualUid2[1]);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index b9908f46c81c..5fd5a7838c3a 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -387,6 +387,59 @@ public class MeasuredEnergyStatsTest {
}
@Test
+ public void testIsValidCustomBucket() {
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3);
+ assertFalse(stats.isValidCustomBucket(-1));
+ assertTrue(stats.isValidCustomBucket(0));
+ assertTrue(stats.isValidCustomBucket(1));
+ assertTrue(stats.isValidCustomBucket(2));
+ assertFalse(stats.isValidCustomBucket(3));
+ assertFalse(stats.isValidCustomBucket(4));
+
+ final MeasuredEnergyStats boringStats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 0);
+ assertFalse(boringStats.isValidCustomBucket(-1));
+ assertFalse(boringStats.isValidCustomBucket(0));
+ assertFalse(boringStats.isValidCustomBucket(1));
+ }
+
+ @Test
+ public void testGetAccumulatedCustomBucketEnergies() {
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3);
+
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
+ stats.updateCustomBucket(2, 13, true);
+ stats.updateCustomBucket(1, 70, true);
+
+ final long[] output = stats.getAccumulatedCustomBucketEnergies();
+ assertEquals(3, output.length);
+
+ assertEquals(50, output[0]);
+ assertEquals(60 + 70, output[1]);
+ assertEquals(13, output[2]);
+ }
+
+ @Test
+ public void testGetAccumulatedCustomBucketEnergies_empty() {
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 0);
+
+ final long[] output = stats.getAccumulatedCustomBucketEnergies();
+ assertEquals(0, output.length);
+ }
+
+ @Test
+ public void testGetNumberCustomEnergyBuckets() {
+ assertEquals(0, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 0)
+ .getNumberCustomEnergyBuckets());
+ assertEquals(3, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3)
+ .getNumberCustomEnergyBuckets());
+ }
+
+ @Test
public void testReset() {
final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
final int numCustomBuckets = 2;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 35e6b8595c2a..40c75a4d2f2f 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1414,6 +1414,16 @@ public class Typeface {
return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
}
+ /** @hide */
+ public List<FontFamily> getFallback() {
+ ArrayList<FontFamily> families = new ArrayList<>();
+ int familySize = nativeGetFamilySize(native_instance);
+ for (int i = 0; i < familySize; ++i) {
+ families.add(new FontFamily(nativeGetFamily(native_instance, i)));
+ }
+ return families;
+ }
+
private static native long nativeCreateFromTypeface(long native_instance, int style);
private static native long nativeCreateFromTypefaceWithExactStyle(
long native_instance, int weight, boolean italic);
@@ -1439,6 +1449,13 @@ public class Typeface {
@CriticalNative
private static native long nativeGetReleaseFunc();
+ @CriticalNative
+ private static native int nativeGetFamilySize(long naitvePtr);
+
+ @CriticalNative
+ private static native long nativeGetFamily(long nativePtr, int index);
+
+
private static native void nativeRegisterGenericFamily(String str, long nativePtr);
private static native int nativeWriteTypefaces(
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 8c13d3e7e51d..a771a6e35345 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -125,7 +125,7 @@ public final class FontFamily {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
- final FontFamily family = new FontFamily(mFonts, ptr);
+ final FontFamily family = new FontFamily(ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
}
@@ -146,7 +146,8 @@ public final class FontFamily {
private final long mNativePtr;
// Use Builder instead.
- private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
+ /** @hide */
+ public FontFamily(long ptr) {
mNativePtr = ptr;
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 904085feb8cb..255f9e659c36 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -68,44 +68,23 @@ public final class SystemFonts {
*/
public static @NonNull Set<Font> getAvailableFonts() {
synchronized (LOCK) {
- if (sAvailableFonts != null) {
- return sAvailableFonts;
- }
-
- if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
- sAvailableFonts = collectAllFonts();
- } else {
+ if (sAvailableFonts == null) {
Set<Font> set = new ArraySet<>();
- for (FontFamily[] items : sFamilyMap.values()) {
- for (FontFamily family : items) {
- for (int i = 0; i < family.getSize(); ++i) {
- set.add(family.getFont(i));
+ for (Typeface tf : Typeface.getSystemFontMap().values()) {
+ List<FontFamily> families = tf.getFallback();
+ for (int i = 0; i < families.size(); ++i) {
+ FontFamily family = families.get(i);
+ for (int j = 0; j < family.getSize(); ++j) {
+ set.add(family.getFont(j));
}
}
}
-
sAvailableFonts = Collections.unmodifiableSet(set);
}
return sAvailableFonts;
}
}
- private static @NonNull Set<Font> collectAllFonts() {
- // TODO: use updated fonts
- FontConfig fontConfig = getSystemPreinstalledFontConfig();
- Map<String, FontFamily[]> map = buildSystemFallback(fontConfig);
-
- Set<Font> res = new ArraySet<>();
- for (FontFamily[] families : map.values()) {
- for (FontFamily family : families) {
- for (int i = 0; i < family.getSize(); ++i) {
- res.add(family.getFont(i));
- }
- }
- }
- return res;
- }
-
private static @Nullable ByteBuffer mmap(@NonNull String fullPath) {
try (FileInputStream file = new FileInputStream(fullPath)) {
final FileChannel fileChannel = file.getChannel();
@@ -329,13 +308,4 @@ public final class SystemFonts {
Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
return result;
}
-
- /**
- * @hide
- */
- public void resetFallbackMapping(Map<String, FontFamily[]> fallbackMap) {
- synchronized (LOCK) {
- sFamilyMap = fallbackMap;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
index aa82339a436a..73fd6931066d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
@@ -16,24 +16,16 @@
package com.android.wm.shell;
-import android.util.Slog;
-
-import com.android.wm.shell.apppairs.AppPairs;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.common.annotations.ExternalThread;
import java.io.PrintWriter;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
/**
* An entry point into the shell for dumping shell internal state and running adb commands.
*
* Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
*/
+@ExternalThread
public interface ShellCommandHandler {
/**
* Dumps the shell state.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 982cc006e331..eaed24d6195a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -18,13 +18,12 @@ package com.android.wm.shell;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
-import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
+import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
@@ -38,24 +37,24 @@ import java.util.Optional;
public final class ShellCommandHandlerImpl {
private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
- private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<Pip> mPipOptional;
- private final Optional<OneHanded> mOneHandedOptional;
- private final Optional<HideDisplayCutout> mHideDisplayCutout;
+ private final Optional<OneHandedController> mOneHandedOptional;
+ private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
+ private final Optional<AppPairsController> mAppPairsOptional;
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<AppPairs> mAppPairsOptional;
private final ShellExecutor mMainExecutor;
private final HandlerImpl mImpl = new HandlerImpl();
public static ShellCommandHandler create(
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional,
+ Optional<OneHandedController> oneHandedOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutout,
+ Optional<AppPairsController> appPairsOptional,
ShellExecutor mainExecutor) {
return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional,
splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
@@ -64,12 +63,12 @@ public final class ShellCommandHandlerImpl {
private ShellCommandHandlerImpl(
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional,
+ Optional<OneHandedController> oneHandedOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutout,
+ Optional<AppPairsController> appPairsOptional,
ShellExecutor mainExecutor) {
mShellTaskOrganizer = shellTaskOrganizer;
mLegacySplitScreenOptional = legacySplitScreenOptional;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index 925bf4bbb01c..7376d9898ab8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -18,13 +18,12 @@ package com.android.wm.shell;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
-import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
@@ -39,9 +38,9 @@ public class ShellInitImpl {
private final DisplayImeController mDisplayImeController;
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
- private final Optional<AppPairs> mAppPairsOptional;
+ private final Optional<AppPairsController> mAppPairsOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
@@ -51,9 +50,9 @@ public class ShellInitImpl {
public static ShellInit create(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairs> appPairsOptional,
+ Optional<AppPairsController> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
ShellExecutor mainExecutor) {
@@ -71,9 +70,9 @@ public class ShellInitImpl {
private ShellInitImpl(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairs> appPairsOptional,
+ Optional<AppPairsController> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
ShellExecutor mainExecutor) {
@@ -97,7 +96,7 @@ public class ShellInitImpl {
// Register the shell organizer
mShellTaskOrganizer.registerOrganizer();
- mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
+ mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered);
mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
// Bind the splitscreen impl to the drag drop controller
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index a570c0af698d..b22f358c0781 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -44,7 +44,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
+import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import java.io.PrintWriter;
@@ -108,20 +108,20 @@ public class ShellTaskOrganizer extends TaskOrganizer {
* compat.
*/
@Nullable
- private final SizeCompatUI mSizeCompatUI;
+ private final SizeCompatUIController mSizeCompatUI;
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
- SizeCompatUI sizeCompatUI) {
+ SizeCompatUIController sizeCompatUI) {
this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context, @Nullable SizeCompatUI sizeCompatUI) {
+ Context context, @Nullable SizeCompatUIController sizeCompatUI) {
super(taskOrganizerController, mainExecutor);
// TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
// by a controller, that class should be create while porting
@@ -342,8 +342,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
/**
- * Notifies {@link SizeCompatUI} about the size compat info changed on the give Task to update
- * the UI accordingly.
+ * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task
+ * to update the UI accordingly.
*
* @param taskInfo the new Task info
* @param taskListener listener to handle the Task Surface placement. {@code null} if task is
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index a5dd79b373bd..58ca1fbaba24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -30,6 +30,7 @@ import java.util.function.Consumer;
public class TaskViewFactoryController {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
+ private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
ShellExecutor shellExecutor) {
@@ -37,8 +38,11 @@ public class TaskViewFactoryController {
mShellExecutor = shellExecutor;
}
+ public TaskViewFactory asTaskViewFactory() {
+ return mImpl;
+ }
+
/** Creates an {@link TaskView} */
- @ShellMainThread
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
TaskView taskView = new TaskView(context, mTaskOrganizer);
executor.execute(() -> {
@@ -46,10 +50,6 @@ public class TaskViewFactoryController {
});
}
- public TaskViewFactory getTaskViewFactory() {
- return new TaskViewFactoryImpl();
- }
-
private class TaskViewFactoryImpl implements TaskViewFactory {
@ExternalThread
public void create(@UiContext Context context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
index abd92577c1d4..59271e9fb63c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
@@ -27,6 +27,7 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener
/**
* The singleton wrapper to communicate between WindowManagerService and WMShell features
* (e.g: PIP, SplitScreen, Bubble, OneHandedMode...etc)
+ * TODO: Remove once PinnedStackListenerForwarder can be removed
*/
public class WindowManagerShellWrapper {
private static final String TAG = WindowManagerShellWrapper.class.getSimpleName();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
index f5aa852c87ae..a9b1dbc3c23b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -35,8 +35,4 @@ public interface AppPairs {
boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2);
/** Unpairs any app-pair containing this task id. */
void unpair(int taskId);
- /** Dumps current status of app pairs. */
- void dump(@NonNull PrintWriter pw, String prefix);
- /** Called when the shell organizer has been registered. */
- void onOrganizerRegistered();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
index e380426b9ca2..0415f12496f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -51,18 +51,7 @@ public class AppPairsController {
private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
private final DisplayController mDisplayController;
- /**
- * Creates {@link AppPairs}, returns {@code null} if the feature is not supported.
- */
- @Nullable
- public static AppPairs create(ShellTaskOrganizer organizer,
- SyncTransactionQueue syncQueue, DisplayController displayController,
- ShellExecutor mainExecutor) {
- return new AppPairsController(organizer, syncQueue, displayController,
- mainExecutor).mImpl;
- }
-
- AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
+ public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
DisplayController displayController, ShellExecutor mainExecutor) {
mTaskOrganizer = organizer;
mSyncQueue = syncQueue;
@@ -70,18 +59,22 @@ public class AppPairsController {
mMainExecutor = mainExecutor;
}
- void onOrganizerRegistered() {
+ public AppPairs asAppPairs() {
+ return mImpl;
+ }
+
+ public void onOrganizerRegistered() {
if (mPairsPool == null) {
setPairsPool(new AppPairsPool(this));
}
}
@VisibleForTesting
- void setPairsPool(AppPairsPool pool) {
+ public void setPairsPool(AppPairsPool pool) {
mPairsPool = pool;
}
- boolean pair(int taskId1, int taskId2) {
+ public boolean pair(int taskId1, int taskId2) {
final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
if (task1 == null || task2 == null) {
@@ -90,13 +83,13 @@ public class AppPairsController {
return pair(task1, task2);
}
- boolean pair(ActivityManager.RunningTaskInfo task1,
+ public boolean pair(ActivityManager.RunningTaskInfo task1,
ActivityManager.RunningTaskInfo task2) {
return pairInner(task1, task2) != null;
}
@VisibleForTesting
- AppPair pairInner(
+ public AppPair pairInner(
@NonNull ActivityManager.RunningTaskInfo task1,
@NonNull ActivityManager.RunningTaskInfo task2) {
final AppPair pair = mPairsPool.acquire();
@@ -109,11 +102,11 @@ public class AppPairsController {
return pair;
}
- void unpair(int taskId) {
+ public void unpair(int taskId) {
unpair(taskId, true /* releaseToPool */);
}
- void unpair(int taskId, boolean releaseToPool) {
+ public void unpair(int taskId, boolean releaseToPool) {
AppPair pair = mActiveAppPairs.get(taskId);
if (pair == null) {
for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
@@ -137,19 +130,19 @@ public class AppPairsController {
}
}
- ShellTaskOrganizer getTaskOrganizer() {
+ public ShellTaskOrganizer getTaskOrganizer() {
return mTaskOrganizer;
}
- SyncTransactionQueue getSyncTransactionQueue() {
+ public SyncTransactionQueue getSyncTransactionQueue() {
return mSyncQueue;
}
- DisplayController getDisplayController() {
+ public DisplayController getDisplayController() {
return mDisplayController;
}
- private void dump(@NonNull PrintWriter pw, String prefix) {
+ public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
@@ -202,21 +195,5 @@ public class AppPairsController {
AppPairsController.this.unpair(taskId);
});
}
-
- @Override
- public void onOrganizerRegistered() {
- mMainExecutor.execute(() -> {
- AppPairsController.this.onOrganizerRegistered();
- });
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, String prefix) {
- try {
- mMainExecutor.executeBlocking(() -> AppPairsController.this.dump(pw, prefix));
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to dump AppPairsController in 2s");
- }
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d73fc6dca4c6..2391a0874bcb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -192,9 +192,9 @@ public class BubbleController {
private boolean mIsStatusBarShade = true;
/**
- * Injected constructor.
+ * Creates an instance of the BubbleController.
*/
- public static Bubbles create(Context context,
+ public static BubbleController create(Context context,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@Nullable IStatusBarService statusBarService,
@@ -211,14 +211,14 @@ public class BubbleController {
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- logger, organizer, positioner, mainExecutor, mainHandler).mImpl;
+ logger, organizer, positioner, mainExecutor, mainHandler);
}
/**
* Testing constructor.
*/
@VisibleForTesting
- public BubbleController(Context context,
+ protected BubbleController(Context context,
BubbleData data,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@@ -322,7 +322,7 @@ public class BubbleController {
}
@VisibleForTesting
- public Bubbles getImpl() {
+ public Bubbles asBubbles() {
return mImpl;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 21004280c952..2f31acd41408 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -163,6 +163,7 @@ public class BubbleExpandedView extends LinearLayout {
// Apply flags to make behaviour match documentLaunchMode=always.
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ fillInIntent.putExtra(Intent.EXTRA_IS_BUBBLED, true);
if (mBubble != null) {
mBubble.setIntentActive();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
index 3a2f0da6bf03..60123ab97fd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
@@ -31,12 +31,6 @@ import java.io.PrintWriter;
public interface HideDisplayCutout {
/**
* Notifies {@link Configuration} changed.
- * @param newConfig
*/
void onConfigurationChanged(Configuration newConfig);
-
- /**
- * Dumps hide display cutout status.
- */
- void dump(@NonNull PrintWriter pw);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
index 12b8b87f1285..23f76ca5f6ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
@@ -44,20 +44,12 @@ public class HideDisplayCutoutController {
@VisibleForTesting
boolean mEnabled;
- HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer,
- ShellExecutor mainExecutor) {
- mContext = context;
- mOrganizer = organizer;
- mMainExecutor = mainExecutor;
- updateStatus();
- }
-
/**
* Creates {@link HideDisplayCutoutController}, returns {@code null} if the feature is not
* supported.
*/
@Nullable
- public static HideDisplayCutout create(
+ public static HideDisplayCutoutController create(
Context context, DisplayController displayController, ShellExecutor mainExecutor) {
// The SystemProperty is set for devices that support this feature and is used to control
// whether to create the HideDisplayCutout instance.
@@ -68,7 +60,19 @@ public class HideDisplayCutoutController {
HideDisplayCutoutOrganizer organizer =
new HideDisplayCutoutOrganizer(context, displayController, mainExecutor);
- return new HideDisplayCutoutController(context, organizer, mainExecutor).mImpl;
+ return new HideDisplayCutoutController(context, organizer, mainExecutor);
+ }
+
+ HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer,
+ ShellExecutor mainExecutor) {
+ mContext = context;
+ mOrganizer = organizer;
+ mMainExecutor = mainExecutor;
+ updateStatus();
+ }
+
+ public HideDisplayCutout asHideDisplayCutout() {
+ return mImpl;
}
@VisibleForTesting
@@ -94,7 +98,7 @@ public class HideDisplayCutoutController {
updateStatus();
}
- private void dump(@NonNull PrintWriter pw) {
+ public void dump(@NonNull PrintWriter pw) {
final String prefix = " ";
pw.print(TAG);
pw.println(" states: ");
@@ -111,14 +115,5 @@ public class HideDisplayCutoutController {
HideDisplayCutoutController.this.onConfigurationChanged(newConfig);
});
}
-
- @Override
- public void dump(@NonNull PrintWriter pw) {
- try {
- mMainExecutor.executeBlocking(() -> HideDisplayCutoutController.this.dump(pw));
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to dump HideDisplayCutoutController in 2s");
- }
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index bca6deb451c9..d25bef197359 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -115,21 +115,6 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
private volatile boolean mAdjustedForIme = false;
private boolean mHomeStackResizable = false;
- /**
- * Creates {@link SplitScreen}, returns {@code null} if the feature is not supported.
- */
- @Nullable
- public static LegacySplitScreen create(Context context,
- DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController imeController, TransactionPool transactionPool,
- ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
- TaskStackListenerImpl taskStackListener, Transitions transitions,
- ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) {
- return new LegacySplitScreenController(context, displayController, systemWindows,
- imeController, transactionPool, shellTaskOrganizer, syncQueue, taskStackListener,
- transitions, mainExecutor, sfVsyncAnimationHandler).mImpl;
- }
-
public LegacySplitScreenController(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController imeController, TransactionPool transactionPool,
@@ -228,8 +213,12 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
});
}
+
+ public LegacySplitScreen asLegacySplitScreen() {
+ return mImpl;
+ }
- void onSplitScreenSupported() {
+ public void onSplitScreenSupported() {
// Set starting tile bounds based on middle target
final WindowContainerTransaction tct = new WindowContainerTransaction();
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
@@ -237,7 +226,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mTaskOrganizer.applyTransaction(tct);
}
- private void onKeyguardVisibilityChanged(boolean showing) {
+ public void onKeyguardVisibilityChanged(boolean showing) {
if (!isSplitActive() || mView == null) {
return;
}
@@ -293,19 +282,19 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- boolean isMinimized() {
+ public boolean isMinimized() {
return mMinimized;
}
- boolean isHomeStackResizable() {
+ public boolean isHomeStackResizable() {
return mHomeStackResizable;
}
- DividerView getDividerView() {
+ public DividerView getDividerView() {
return mView;
}
- boolean isDividerVisible() {
+ public boolean isDividerVisible() {
return mView != null && mView.getVisibility() == View.VISIBLE;
}
@@ -314,13 +303,13 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
* isDividerVisible because the divider is only visible once *everything* is in split mode
* while this only cares if some things are (eg. while entering/exiting as well).
*/
- private boolean isSplitActive() {
+ public boolean isSplitActive() {
return mSplits.mPrimary != null && mSplits.mSecondary != null
&& (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED
|| mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED);
}
- private void addDivider(Configuration configuration) {
+ public void addDivider(Configuration configuration) {
Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
mView = (DividerView)
LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
@@ -338,14 +327,14 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mWindowManager.add(mView, width, height, mContext.getDisplayId());
}
- private void removeDivider() {
+ public void removeDivider() {
if (mView != null) {
mView.onDividerRemoved();
}
mWindowManager.remove();
}
- private void update(Configuration configuration) {
+ public void update(Configuration configuration) {
final boolean isDividerHidden = mView != null && mIsKeyguardShowing;
removeDivider();
@@ -358,11 +347,11 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mView.setHidden(isDividerHidden);
}
- void onTaskVanished() {
+ public void onTaskVanished() {
removeDivider();
}
- private void updateVisibility(final boolean visible) {
+ public void updateVisibility(final boolean visible) {
if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
if (mVisible != visible) {
mVisible = visible;
@@ -390,7 +379,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- private void setMinimized(final boolean minimized) {
+ public void setMinimized(final boolean minimized) {
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
mMainExecutor.execute(() -> {
if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
@@ -401,7 +390,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
});
}
- private void setHomeMinimized(final boolean minimized) {
+ public void setHomeMinimized(final boolean minimized) {
if (DEBUG) {
Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:"
+ mHomeStackResizable + " split:" + isDividerVisible());
@@ -441,7 +430,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void setAdjustedForIme(boolean adjustedForIme) {
+ public void setAdjustedForIme(boolean adjustedForIme) {
if (mAdjustedForIme == adjustedForIme) {
return;
}
@@ -449,30 +438,30 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
updateTouchable();
}
- private void updateTouchable() {
+ public void updateTouchable() {
mWindowManager.setTouchable(!mAdjustedForIme);
}
- private void onUndockingTask() {
+ public void onUndockingTask() {
if (mView != null) {
mView.onUndockingTask();
}
}
- private void onAppTransitionFinished() {
+ public void onAppTransitionFinished() {
if (mView == null) {
return;
}
mForcedResizableController.onAppTransitionFinished();
}
- private void dump(PrintWriter pw) {
+ public void dump(PrintWriter pw) {
pw.print(" mVisible="); pw.println(mVisible);
pw.print(" mMinimized="); pw.println(mMinimized);
pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
}
- long getAnimDuration() {
+ public long getAnimDuration() {
float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
Settings.Global.TRANSITION_ANIMATION_SCALE,
mContext.getResources().getFloat(
@@ -482,14 +471,14 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
return (long) (transitionDuration * transitionScale);
}
- void registerInSplitScreenListener(Consumer<Boolean> listener) {
+ public void registerInSplitScreenListener(Consumer<Boolean> listener) {
listener.accept(isDividerVisible());
synchronized (mDockedStackExistsListeners) {
mDockedStackExistsListeners.add(new WeakReference<>(listener));
}
}
- void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
+ public void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
synchronized (mDockedStackExistsListeners) {
for (int i = mDockedStackExistsListeners.size() - 1; i >= 0; i--) {
if (mDockedStackExistsListeners.get(i) == listener) {
@@ -499,13 +488,13 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- private void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
+ public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
synchronized (mBoundsChangedListeners) {
mBoundsChangedListeners.add(new WeakReference<>(listener));
}
}
- private boolean splitPrimaryTask() {
+ public boolean splitPrimaryTask() {
try {
if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED
|| isSplitActive()) {
@@ -538,12 +527,12 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
topRunningTask.taskId, true /* onTop */);
}
- private void dismissSplitToPrimaryTask() {
+ public void dismissSplitToPrimaryTask() {
startDismissSplit(true /* toPrimaryTask */);
}
/** Notifies the bounds of split screen changed. */
- void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
+ public void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
synchronized (mBoundsChangedListeners) {
mBoundsChangedListeners.removeIf(wf -> {
BiConsumer<Rect, Rect> l = wf.get();
@@ -553,19 +542,19 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void startEnterSplit() {
+ public void startEnterSplit() {
update(mDisplayController.getDisplayContext(
mContext.getDisplayId()).getResources().getConfiguration());
// Set resizable directly here because applyEnterSplit already resizes home stack.
mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
}
- void prepareEnterSplitTransition(WindowContainerTransaction outWct) {
+ public void prepareEnterSplitTransition(WindowContainerTransaction outWct) {
// Set resizable directly here because buildEnterSplit already resizes home stack.
mHomeStackResizable = mWindowManagerProxy.buildEnterSplit(outWct, mSplits, mSplitLayout);
}
- void finishEnterSplitTransition(boolean minimized) {
+ public void finishEnterSplitTransition(boolean minimized) {
update(mDisplayController.getDisplayContext(
mContext.getDisplayId()).getResources().getConfiguration());
if (minimized) {
@@ -575,11 +564,11 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void startDismissSplit(boolean toPrimaryTask) {
+ public void startDismissSplit(boolean toPrimaryTask) {
startDismissSplit(toPrimaryTask, false /* snapped */);
}
- void startDismissSplit(boolean toPrimaryTask, boolean snapped) {
+ public void startDismissSplit(boolean toPrimaryTask, boolean snapped) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mSplits.getSplitTransitions().dismissSplit(
mSplits, mSplitLayout, !toPrimaryTask, snapped);
@@ -589,7 +578,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void onDismissSplit() {
+ public void onDismissSplit() {
updateVisibility(false /* visible */);
mMinimized = false;
// Resets divider bar position to undefined, so new divider bar will apply default position
@@ -599,7 +588,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mImePositionProcessor.reset();
}
- void ensureMinimizedSplit() {
+ public void ensureMinimizedSplit() {
setHomeMinimized(true /* minimized */);
if (mView != null && !isDividerVisible()) {
// Wasn't in split-mode yet, so enter now.
@@ -610,7 +599,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void ensureNormalSplit() {
+ public void ensureNormalSplit() {
setHomeMinimized(false /* minimized */);
if (mView != null && !isDividerVisible()) {
// Wasn't in split-mode, so enter now.
@@ -621,15 +610,15 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- LegacySplitDisplayLayout getSplitLayout() {
+ public LegacySplitDisplayLayout getSplitLayout() {
return mSplitLayout;
}
- WindowManagerProxy getWmProxy() {
+ public WindowManagerProxy getWmProxy() {
return mWindowManagerProxy;
}
- WindowContainerToken getSecondaryRoot() {
+ public WindowContainerToken getSecondaryRoot() {
if (mSplits == null || mSplits.mSecondary == null) {
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index e95864873c0c..11c11f44a781 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -69,9 +69,4 @@ public interface OneHanded {
* 3 button navigation mode only
*/
void registerGestureCallback(OneHandedGestureEventCallback callback);
-
- /**
- * Dump one handed status.
- */
- void dump(@NonNull PrintWriter pw);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index eaa704f22410..5a3c38b09ec6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -134,10 +134,10 @@ public class OneHandedController {
/**
- * Creates {@link OneHanded}, returns {@code null} if the feature is not supported.
+ * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
*/
@Nullable
- public static OneHanded create(
+ public static OneHandedController create(
Context context, DisplayController displayController,
TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger,
ShellExecutor mainExecutor, Handler mainHandler) {
@@ -166,7 +166,7 @@ public class OneHandedController {
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
gestureHandler, timeoutHandler, oneHandedUiEventsLogger, overlayManager,
- taskStackListener, mainExecutor, mainHandler).mImpl;
+ taskStackListener, mainExecutor, mainHandler);
}
@VisibleForTesting
@@ -228,6 +228,10 @@ public class OneHandedController {
mAccessibilityStateChangeListener);
}
+ public OneHanded asOneHanded() {
+ return mImpl;
+ }
+
/**
* Set one handed enabled or disabled when user update settings
*/
@@ -468,7 +472,7 @@ public class OneHandedController {
}
}
- private void dump(@NonNull PrintWriter pw) {
+ public void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mOffSetFraction=");
@@ -561,12 +565,5 @@ public class OneHandedController {
OneHandedController.this.registerGestureCallback(callback);
});
}
-
- @Override
- public void dump(@NonNull PrintWriter pw) {
- mMainExecutor.execute(() -> {
- OneHandedController.this.dump(pw);
- });
- }
}
}
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 ad6f435ea907..3064af6f5170 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
@@ -67,6 +67,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.transition.Transitions;
@@ -131,7 +132,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -207,7 +208,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -1047,7 +1048,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/**
- * Sync with {@link LegacySplitScreen} on destination bounds if PiP is going to split screen.
+ * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
+ * screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
@@ -1057,7 +1059,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return false;
}
- LegacySplitScreen legacySplitScreen = mSplitScreenOptional.get();
+ LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
if (!legacySplitScreen.isDividerVisible()) {
// fail early if system is not in split screen mode
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
deleted file mode 100644
index 11f22ed24a69..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.os.IBinder;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.annotations.ExternalThread;
-
-/**
- * Interface to engage size compat mode UI.
- */
-@ExternalThread
-public interface SizeCompatUI {
- /**
- * Called when the Task info changed. Creates and updates the restart button if there is an
- * activity in size compat, or removes the restart button if there is no size compat activity.
- *
- * @param displayId display the task and activity are in.
- * @param taskId task the activity is in.
- * @param taskBounds task bounds to place the restart button in.
- * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the
- * top activity in this Task is not in size compat.
- * @param taskListener listener to handle the Task Surface placement.
- */
- void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
- @Nullable IBinder sizeCompatActivity,
- @Nullable ShellTaskOrganizer.TaskListener taskListener);
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index 286c3b6a051e..48ee86c4954f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -48,7 +48,6 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
@VisibleForTesting
- final SizeCompatUI mImpl = new SizeCompatUIImpl();
private final Context mContext;
private final ShellExecutor mMainExecutor;
private final DisplayController mDisplayController;
@@ -57,17 +56,8 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
/** Only show once automatically in the process life. */
private boolean mHasShownHint;
- /** Creates the {@link SizeCompatUIController}. */
- public static SizeCompatUI create(Context context,
- DisplayController displayController,
- DisplayImeController imeController,
- ShellExecutor mainExecutor) {
- return new SizeCompatUIController(context, displayController, imeController, mainExecutor)
- .mImpl;
- }
-
@VisibleForTesting
- SizeCompatUIController(Context context,
+ public SizeCompatUIController(Context context,
DisplayController displayController,
DisplayImeController imeController,
ShellExecutor mainExecutor) {
@@ -79,7 +69,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
mImeController.addPositionProcessor(this);
}
- private void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
+ public void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
@Nullable IBinder sizeCompatActivity,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
// TODO Draw button on Task surface
@@ -177,15 +167,4 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
}
return context;
}
-
- private class SizeCompatUIImpl implements SizeCompatUI {
- @Override
- public void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
- @Nullable IBinder sizeCompatActivity,
- @Nullable ShellTaskOrganizer.TaskListener taskListener) {
- mMainExecutor.execute(() ->
- SizeCompatUIController.this.onSizeCompatInfoChanged(displayId, taskId,
- taskBounds, sizeCompatActivity, taskListener));
- }
- }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 111362a93495..ecc066be734f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -24,7 +24,6 @@ import android.os.SystemClock
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
-import com.android.server.wm.flicker.helpers.closePipWindow
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
@@ -113,7 +112,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
if (isTelevision) {
uiDevice.closeTvPipWindow()
} else {
- uiDevice.closePipWindow()
+ closePipWindow(WindowManagerStateHelper(mInstrumentation))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index a14b46ef7a3d..d56ed02972fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -16,13 +16,11 @@
package com.android.wm.shell.flicker.pip
-import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -50,8 +48,7 @@ class EnterExitPipTest(
@JvmStatic
fun getParams(): List<Array<Any>> {
val testApp = FixedAppHelper(instrumentation)
- val baseConfig = getTransitionLaunch(eachRun = true)
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ val testSpec = getTransition(eachRun = true) { configuration ->
setup {
eachRun {
testApp.launchViaIntent(wmHelper)
@@ -97,7 +94,7 @@ class EnterExitPipTest(
}
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
testSpec, supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 99a40daa027f..ff31ba7d2c01 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,13 +16,11 @@
package com.android.wm.shell.flicker.pip
-import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
@@ -50,9 +48,8 @@ class EnterPipTest(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val baseConfig = getTransitionLaunch(
- eachRun = true, stringExtras = emptyMap())
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ val testSpec = getTransition(eachRun = true,
+ stringExtras = emptyMap()) { configuration ->
transitions {
pipApp.clickEnterPipButton()
pipApp.expandPipWindow(wmHelper)
@@ -92,7 +89,7 @@ class EnterPipTest(
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
testSpec, supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 7576e24ace19..f054e6412080 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,13 +16,11 @@
package com.android.wm.shell.flicker.pip
-import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
@@ -48,8 +46,7 @@ class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTest
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val imeApp = ImeAppHelper(instrumentation)
- val baseConfig = getTransitionLaunch(eachRun = false)
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ val testSpec = getTransition(eachRun = false) { configuration ->
setup {
test {
imeApp.launchViaIntent(wmHelper)
@@ -90,7 +87,7 @@ class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTest
}
return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- baseConfig, testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index adab5e81b32d..ade65ac8aa63 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -16,13 +16,11 @@
package com.android.wm.shell.flicker.pip
-import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
@@ -55,8 +53,7 @@ class PipRotationTest(
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val fixedApp = FixedAppHelper(instrumentation)
- val baseConfig = getTransitionLaunch(eachRun = false)
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ val testSpec = getTransition(eachRun = false) { configuration ->
setup {
test {
fixedApp.launchViaIntent(wmHelper)
@@ -112,8 +109,7 @@ class PipRotationTest(
}
return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
- baseConfig, testSpec,
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
repetitions = 5)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 4b826ffd646d..f2d58997d1f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -16,13 +16,11 @@
package com.android.wm.shell.flicker.pip
-import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -52,8 +50,7 @@ class PipToAppTest(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val baseConfig = getTransitionLaunch(eachRun = true)
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ val testSpec = getTransition(eachRun = true) { configuration ->
setup {
eachRun {
this.setRotation(configuration.startRotation)
@@ -110,7 +107,7 @@ class PipToAppTest(
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
index 62e82212b1d1..1b44377425db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
@@ -16,13 +16,11 @@
package com.android.wm.shell.flicker.pip
-import android.os.Bundle
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -52,8 +50,7 @@ class PipToHomeTest(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val baseConfig = getTransitionLaunch(eachRun = true)
- val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ val testSpec = getTransition(eachRun = true) { configuration ->
setup {
eachRun {
this.setRotation(configuration.startRotation)
@@ -111,7 +108,7 @@ class PipToHomeTest(
}
}
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
index eb7bae160577..b1e404e4c8e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
@@ -22,8 +22,6 @@ import android.os.Bundle
import android.view.Surface
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
@@ -83,10 +81,6 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation)
}
test {
removeAllTasksButHome()
-
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
pipApp.exit()
}
}
@@ -98,11 +92,13 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation)
*
* @param eachRun If the pip app should be launched in each run (otherwise only 1x per test)
* @param stringExtras Arguments to pass to the PIP launch intent
+ * @param extraSpec Addicional segment of flicker specification
*/
@JvmOverloads
- fun getTransitionLaunch(
+ open fun getTransition(
eachRun: Boolean,
- stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true")
+ stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
+ extraSpec: FlickerBuilder.(Bundle) -> Unit = {}
): FlickerBuilder.(Bundle) -> Unit {
return { configuration ->
setupAndTeardown(this, configuration)
@@ -135,6 +131,8 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation)
removeAllTasksButHome()
}
}
+
+ extraSpec(this, configuration)
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 80ea9b9e177e..176b33dda020 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -51,7 +51,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
+import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import org.junit.Before;
import org.junit.Test;
@@ -76,7 +76,7 @@ public class ShellTaskOrganizerTests {
@Mock
private Context mContext;
@Mock
- private SizeCompatUI mSizeCompatUI;
+ private SizeCompatUIController mSizeCompatUI;
ShellTaskOrganizer mOrganizer;
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 9430af946899..d10c03677d30 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import org.junit.Before;
@@ -70,7 +71,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen;
+ @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
index 98f01ff08deb..0eb64e5963d1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
@@ -86,7 +86,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
final Rect taskBounds = new Rect(0, 0, 1000, 2000);
// Verify that the restart button is added with non-null size compat activity.
- mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
mMockActivityToken, mMockTaskListener);
mShellMainExecutor.flushAll();
@@ -94,7 +94,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
// Verify that the restart button is removed with null size compat activity.
- mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null);
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null);
mShellMainExecutor.flushAll();
verify(mMockButton).remove();
@@ -104,7 +104,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
public void testChangeButtonVisibilityOnImeShowHide() {
final int taskId = 12;
final Rect taskBounds = new Rect(0, 0, 1000, 2000);
- mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
mMockActivityToken, mMockTaskListener);
mShellMainExecutor.flushAll();
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 8f455fe4ab43..18423562bc56 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -355,29 +355,41 @@ static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring f
env->SetStaticObjectField(cls, fid, typeface);
}
+// Critical Native
+static jint Typeface_getFamilySize(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
+ return toTypeface(faceHandle)->fFontCollection->getFamilies().size();
+}
+
+// Critical Native
+static jlong Typeface_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle, jint index) {
+ std::shared_ptr<minikin::FontFamily> family =
+ toTypeface(faceHandle)->fFontCollection->getFamilies()[index];
+ return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
+}
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gTypefaceMethods[] = {
- { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
- { "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J",
- (void*)Typeface_createFromTypefaceWithExactStyle },
- { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
- (void*)Typeface_createFromTypefaceWithVariation },
- { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias },
- { "nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc },
- { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle },
- { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight },
- { "nativeCreateFromArray", "([JJII)J",
- (void*)Typeface_createFromArray },
- { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault },
- { "nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes },
- { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
- (void*)Typeface_registerGenericFamily },
- { "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
- { "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
- { "nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
- (void*)Typeface_forceSetStaticFinalField },
+ {"nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface},
+ {"nativeCreateFromTypefaceWithExactStyle", "(JIZ)J",
+ (void*)Typeface_createFromTypefaceWithExactStyle},
+ {"nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
+ (void*)Typeface_createFromTypefaceWithVariation},
+ {"nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias},
+ {"nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc},
+ {"nativeGetStyle", "(J)I", (void*)Typeface_getStyle},
+ {"nativeGetWeight", "(J)I", (void*)Typeface_getWeight},
+ {"nativeCreateFromArray", "([JJII)J", (void*)Typeface_createFromArray},
+ {"nativeSetDefault", "(J)V", (void*)Typeface_setDefault},
+ {"nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes},
+ {"nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
+ (void*)Typeface_registerGenericFamily},
+ {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
+ {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+ {"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
+ (void*)Typeface_forceSetStaticFinalField},
+ {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
+ {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
};
int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index d291ec001daf..438a92ef8fb5 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -95,7 +95,7 @@ cc_test {
name: "libincident_test",
test_config: "AndroidTest.xml",
defaults: ["libincidentpriv_defaults"],
- test_suites: ["device-tests", "mts"],
+ test_suites: ["device-tests", "mts-statsd"],
compile_multilib: "both",
multilib: {
lib64: {
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
index 85b4ba54de63..3c48f8f602df 100644
--- a/media/java/android/media/DrmInitData.java
+++ b/media/java/android/media/DrmInitData.java
@@ -19,6 +19,7 @@ import android.annotation.NonNull;
import android.media.MediaDrm;
import java.util.Arrays;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -94,9 +95,9 @@ public abstract class DrmInitData {
* @param data The initialization data.
*/
public SchemeInitData(@NonNull UUID uuid, @NonNull String mimeType, @NonNull byte[] data) {
- this.uuid = uuid;
- this.mimeType = mimeType;
- this.data = data;
+ this.uuid = Objects.requireNonNull(uuid);
+ this.mimeType = Objects.requireNonNull(mimeType);
+ this.data = Objects.requireNonNull(data);
}
@Override
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 98b9ad8ec2b3..740bc2dbeb43 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2526,6 +2526,7 @@ public final class TvInputManager {
* Pauses TV program recording in the current recording session.
*
* @param params A set of extra parameters which might be handled with this event.
+ * {@link TvRecordingClient#pauseRecording(Bundle)}.
*/
void pauseRecording(@NonNull Bundle params) {
if (mToken == null) {
@@ -2543,6 +2544,7 @@ public final class TvInputManager {
* Resumes TV program recording in the current recording session.
*
* @param params A set of extra parameters which might be handled with this event.
+ * {@link TvRecordingClient#resumeRecording(Bundle)}.
*/
void resumeRecording(@NonNull Bundle params) {
if (mToken == null) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 65b64d7e8df3..4972529eb705 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -180,6 +180,10 @@ cc_library_shared {
"libstagefright_foundation_headers",
],
+ // TunerService is a system service required for Tuner feature.
+ // TunerJNI is a client of TunerService so we build the dependency here.
+ required: ["mediatuner"],
+
export_include_dirs: ["."],
cflags: [
diff --git a/media/jni/tuner/ClientHelper.h b/media/jni/tuner/ClientHelper.h
index 185b2f651d29..508dccf36841 100644
--- a/media/jni/tuner/ClientHelper.h
+++ b/media/jni/tuner/ClientHelper.h
@@ -19,6 +19,7 @@
#include <android/binder_parcel_utils.h>
#include <android/hardware/tv/tuner/1.1/types.h>
+#include <utils/Log.h>
using Status = ::ndk::ScopedAStatus;
@@ -37,6 +38,7 @@ public:
} else if (s.isOk()) {
return Result::SUCCESS;
}
+ ALOGE("Aidl exception code %s", s.getDescription().c_str());
return Result::UNKNOWN_ERROR;
}
};
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 8b4ca371056e..f61889035432 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -43,6 +43,7 @@ using ::android::hardware::tv::tuner::V1_0::DemuxStreamId;
using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
using ::android::hardware::tv::tuner::V1_1::ScramblingStatus;
namespace android {
@@ -480,7 +481,7 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v4: {
int size = ipAddr.srcIpAddress.v4().size();
srcIpAddress.isIpV6 = false;
- srcIpAddress.addr.resize(ipAddr.srcIpAddress.v4().size());
+ srcIpAddress.addr.resize(size);
copy(&ipAddr.srcIpAddress.v4()[0], &ipAddr.srcIpAddress.v4()[size],
srcIpAddress.addr.begin());
break;
@@ -493,8 +494,6 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
srcIpAddress.addr.begin());
break;
}
- default:
- break;
}
switch (ipAddr.dstIpAddress.getDiscriminator()) {
case DemuxIpAddress::DstIpAddress::hidl_discriminator::v4: {
@@ -513,8 +512,6 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
dstIpAddress.addr.begin());
break;
}
- default:
- break;
}
}
@@ -696,8 +693,6 @@ void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& fil
getHidlRestartEvent(filterEvents, eventExt);
break;
}
- default:
- break;
}
}
@@ -883,19 +878,18 @@ void TunerFilterCallback::getHidlMonitorEvent(const vector<TunerFilterEvent>& fi
DemuxFilterEventExt& eventExt) {
auto monitor = filterEvents[0].get<TunerFilterEvent::monitor>();
eventExt.events.resize(1);
+ DemuxFilterMonitorEvent monitorEvent;
switch (monitor.getTag()) {
case TunerFilterMonitorEvent::scramblingStatus: {
- eventExt.events[0].monitorEvent().scramblingStatus(
- static_cast<ScramblingStatus>(monitor.scramblingStatus));
+ monitorEvent.scramblingStatus(static_cast<ScramblingStatus>(monitor.scramblingStatus));
+ eventExt.events[0].monitorEvent(monitorEvent);
break;
}
case TunerFilterMonitorEvent::cid: {
- eventExt.events[0].monitorEvent().cid(static_cast<uint32_t>(monitor.cid));
+ monitorEvent.cid(static_cast<uint32_t>(monitor.cid));
+ eventExt.events[0].monitorEvent(monitorEvent);
break;
}
- default:
- eventExt.events[0].noinit();
- break;
}
}
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 3a00133c69e2..0613223bd5dc 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -49,9 +49,12 @@ using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendModulationStatus;
using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusAtsc3PlpInfo;
using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
@@ -61,19 +64,22 @@ using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendInterleaveMode;
using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendRollOff;
using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode;
using ::android::hardware::tv::tuner::V1_1::FrontendType;
namespace android {
/////////////// FrontendClient ///////////////////////
-FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type) {
+FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type) {
mTunerFrontend = tunerFrontend;
mAidlCallback = NULL;
mHidlCallback = NULL;
- mId = id;
mType = type;
}
@@ -104,6 +110,11 @@ void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) {
mFrontend_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFrontend);
}
+// TODO: move after migration is done
+void FrontendClient::setId(int id) {
+ mId = id;
+}
+
Result FrontendClient::tune(const FrontendSettings& settings,
const FrontendSettingsExt1_1& settingsExt1_1) {
if (mTunerFrontend != NULL) {
@@ -333,13 +344,26 @@ shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
}
int FrontendClient::getId() {
- return mId;
+ if (mTunerFrontend != NULL) {
+ Status s = mTunerFrontend->getFrontendId(&mId);
+ if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
+ return mId;
+ }
+ ALOGE("Failed to getFrontendId from Tuner Frontend");
+ return -1;
+ }
+
+ if (mFrontend != NULL) {
+ return mId;
+ }
+
+ return -1;
}
vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) {
vector<FrontendStatus> hidlStatus;
for (TunerFrontendStatus s : aidlStatus) {
- FrontendStatus status;
+ FrontendStatus status = FrontendStatus();
switch (s.getTag()) {
case TunerFrontendStatus::isDemodLocked: {
status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
@@ -389,25 +413,31 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>
}
case TunerFrontendStatus::modulation: {
auto aidlMod = s.get<TunerFrontendStatus::modulation>();
+ FrontendModulationStatus modulation;
switch (mType) {
case (int)FrontendType::DVBC:
- status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ modulation.dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DVBS:
- status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ modulation.dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS:
- status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ modulation.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS3:
- status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ modulation.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ modulation.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
default:
@@ -466,7 +496,7 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>
}
case TunerFrontendStatus::hierarchy: {
status.hierarchy(static_cast<FrontendDvbtHierarchy>(
- s.get<TunerFrontendStatus::freqOffset>()));
+ s.get<TunerFrontendStatus::hierarchy>()));
hidlStatus.push_back(status);
break;
}
@@ -477,15 +507,16 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>
}
case TunerFrontendStatus::plpInfo: {
int size = s.get<TunerFrontendStatus::plpInfo>().size();
- status.plpInfo().resize(size);
+ hidl_vec<FrontendStatusAtsc3PlpInfo> info(size);
for (int i = 0; i < size; i++) {
auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i];
- status.plpInfo()[i] = {
+ info[i] = {
.plpId = (uint8_t)aidlInfo.plpId,
.isLocked = aidlInfo.isLocked,
.uec = (uint32_t)aidlInfo.uec,
};
}
+ status.plpInfo(info);
hidlStatus.push_back(status);
break;
}
@@ -503,52 +534,54 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
FrontendStatusExt1_1 status;
switch (s.getTag()) {
case TunerFrontendStatus::modulations: {
+ vector<FrontendModulation> ms;
for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
- int size = status.modulations().size();
- status.modulations().resize(size + 1);
+ FrontendModulation m;
switch (mType) {
case (int)FrontendType::DVBC:
- status.modulations()[size].dvbc(
- static_cast<FrontendDvbcModulation>(aidlMod));
+ m.dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::DVBS:
- status.modulations()[size].dvbs(
- static_cast<FrontendDvbsModulation>(aidlMod));
+ m.dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::DVBT:
- status.modulations()[size].dvbt(
- static_cast<FrontendDvbtConstellation>(aidlMod));
+ m.dvbt(static_cast<FrontendDvbtConstellation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ISDBS:
- status.modulations()[size].isdbs(
- static_cast<FrontendIsdbsModulation>(aidlMod));
+ m.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ISDBS3:
- status.modulations()[size].isdbs3(
- static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ m.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ISDBT:
- status.modulations()[size].isdbt(
- static_cast<FrontendIsdbtModulation>(aidlMod));
+ m.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ATSC:
- status.modulations()[size].atsc(
- static_cast<FrontendAtscModulation>(aidlMod));
+ m.atsc(static_cast<FrontendAtscModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ATSC3:
- status.modulations()[size].atsc3(
- static_cast<FrontendAtsc3Modulation>(aidlMod));
+ m.atsc3(static_cast<FrontendAtsc3Modulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::DTMB:
- status.modulations()[size].dtmb(
- static_cast<FrontendDtmbModulation>(aidlMod));
+ m.dtmb(static_cast<FrontendDtmbModulation>(aidlMod));
+ ms.push_back(m);
break;
default:
- status.modulations().resize(size);
break;
}
}
- hidlStatus.push_back(status);
+ if (ms.size() > 0) {
+ status.modulations(ms);
+ hidlStatus.push_back(status);
+ }
break;
}
case TunerFrontendStatus::bers: {
@@ -571,25 +604,31 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::bandwidth: {
auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
+ FrontendBandwidth band;
switch (mType) {
case (int)FrontendType::ATSC3:
- status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+ band.atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DVBC:
- status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+ band.dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DVBT:
- status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+ band.dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+ band.isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DTMB:
- status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+ band.dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
default:
@@ -599,17 +638,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::interval: {
auto aidlInter = s.get<TunerFrontendStatus::interval>();
+ FrontendGuardInterval inter;
switch (mType) {
case (int)FrontendType::DVBT:
- status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+ inter.dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+ status.interval(inter);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+ inter.isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+ status.interval(inter);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DTMB:
- status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+ inter.dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+ status.interval(inter);
hidlStatus.push_back(status);
break;
default:
@@ -619,19 +662,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::transmissionMode: {
auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
+ FrontendTransmissionMode trans;
switch (mType) {
case (int)FrontendType::DVBT:
- status.transmissionMode().dvbt(
- static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+ trans.dvbt(static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+ status.transmissionMode(trans);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+ trans.isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+ status.transmissionMode(trans);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DTMB:
- status.transmissionMode().dtmb(
- static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+ trans.dtmb(static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+ status.transmissionMode(trans);
hidlStatus.push_back(status);
break;
default:
@@ -650,28 +695,30 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
break;
}
case TunerFrontendStatus::interleaving: {
+ vector<FrontendInterleaveMode> modes;
for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
- int size = status.interleaving().size();
- status.interleaving().resize(size + 1);
+ FrontendInterleaveMode mode;
switch (mType) {
case (int)FrontendType::DVBC:
- status.interleaving()[size].dvbc(
- static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+ mode.dvbc(static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+ modes.push_back(mode);
break;
case (int)FrontendType::ATSC3:
- status.interleaving()[size].atsc3(
- static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+ mode.atsc3(static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+ modes.push_back(mode);
break;
case (int)FrontendType::DTMB:
- status.interleaving()[size].dtmb(
- static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+ mode.dtmb(static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+ modes.push_back(mode);
break;
default:
- status.interleaving().resize(size);
break;
}
}
- hidlStatus.push_back(status);
+ if (modes.size() > 0) {
+ status.interleaving(modes);
+ hidlStatus.push_back(status);
+ }
break;
}
case TunerFrontendStatus::isdbtSegment: {
@@ -690,17 +737,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::rollOff: {
auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
+ FrontendRollOff roll;
switch (mType) {
case (int)FrontendType::DVBS:
- status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+ roll.dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+ status.rollOff(roll);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS:
- status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+ roll.isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+ status.rollOff(roll);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS3:
- status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+ roll.isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+ status.rollOff(roll);
hidlStatus.push_back(status);
break;
default:
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 298b3974aeb9..f71616cb32b7 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -108,7 +108,7 @@ private:
struct FrontendClient : public RefBase {
public:
- FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type);
+ FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type);
~FrontendClient();
/**
@@ -180,6 +180,7 @@ public:
shared_ptr<ITunerFrontend> getAidlFrontend();
+ void setId(int id);
int getId();
private:
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 7f954b561567..cf17ed6f4383 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -46,13 +46,12 @@ TunerClient::TunerClient() {
// Connect with Tuner Service.
::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
mTunerService = ITunerService::fromBinder(binder);
- // TODO: Remove after JNI migration is done.
- mTunerService = NULL;
if (mTunerService == NULL) {
ALOGE("Failed to get tuner service");
} else {
// TODO: b/178124017 update TRM in TunerService independently.
mTunerService->updateTunerResources();
+ mTunerService->getTunerHalVersion(&mTunerVersion);
}
}
@@ -115,7 +114,7 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) {
if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
return NULL;
}
- return new FrontendClient(tunerFrontend, frontendHandle, aidlFrontendInfo.type);
+ return new FrontendClient(tunerFrontend, aidlFrontendInfo.type);
}
if (mTuner != NULL) {
@@ -127,8 +126,10 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) {
if (res != Result::SUCCESS) {
return NULL;
}
- sp<FrontendClient> frontendClient = new FrontendClient(NULL, id, (int)hidlInfo.type);
+ sp<FrontendClient> frontendClient = new FrontendClient(
+ NULL, (int)hidlInfo.type);
frontendClient->setHidlFrontend(hidlFrontend);
+ frontendClient->setId(id);
return frontendClient;
}
}
@@ -358,7 +359,7 @@ void TunerClient::updateLnbResources() {
sp<ITuner> TunerClient::getHidlTuner() {
if (mTuner == NULL) {
- mTunerVersion = 0;
+ mTunerVersion = TUNER_HAL_VERSION_UNKNOWN;
mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService();
if (mTuner_1_1 == NULL) {
@@ -367,11 +368,11 @@ sp<ITuner> TunerClient::getHidlTuner() {
if (mTuner == NULL) {
ALOGW("Failed to get tuner 1.0 service.");
} else {
- mTunerVersion = 1 << 16;
+ mTunerVersion = TUNER_HAL_VERSION_1_0;
}
} else {
mTuner = static_cast<sp<ITuner>>(mTuner_1_1);
- mTunerVersion = ((1 << 16) | 1);
+ mTunerVersion = TUNER_HAL_VERSION_1_1;
}
}
return mTuner;
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 744bf2058766..9671cf787e3e 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -48,6 +48,10 @@ using namespace std;
namespace android {
+const static int TUNER_HAL_VERSION_UNKNOWN = 0;
+const static int TUNER_HAL_VERSION_1_0 = 1 << 16;
+const static int TUNER_HAL_VERSION_1_1 = (1 << 16) | 1;
+
typedef enum {
FRONTEND,
LNB,
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
index 09f285b445e9..14a7cfa568d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
@@ -65,7 +65,7 @@ public class MobileMappings {
return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus";
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
return toIconKey(TelephonyManager.NETWORK_TYPE_NR);
- case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE:
+ case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED:
return toIconKey(TelephonyManager.NETWORK_TYPE_NR) + "_Plus";
default:
return "unsupported";
@@ -180,7 +180,7 @@ public class MobileMappings {
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA),
TelephonyIcons.NR_5G);
networkToIconLookup.put(toDisplayIconKey(
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE),
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED),
TelephonyIcons.NR_5G_PLUS);
networkToIconLookup.put(toIconKey(
TelephonyManager.NETWORK_TYPE_NR),
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_inner.xml b/packages/SystemUI/res/color/kg_user_avatar_frame.xml
index 4c1042e70c1a..174981e2a660 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_inner.xml
+++ b/packages/SystemUI/res/color/kg_user_avatar_frame.xml
@@ -14,14 +14,10 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.keyguard.AlphaOptimizedLinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_user_switcher_inner"
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
- android:layout_gravity="end"
- android:gravity="end"
- android:paddingTop="4dp">
-</com.android.keyguard.AlphaOptimizedLinearLayout>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_activated="true"
+ android:color="@color/kg_user_switcher_avatar_background" />
+ <item android:color="@color/kg_user_switcher_avatar_background" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/end_guest_button_background.xml b/packages/SystemUI/res/drawable/end_guest_button_background.xml
new file mode 100644
index 000000000000..5644b657a609
--- /dev/null
+++ b/packages/SystemUI/res/drawable/end_guest_button_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:width="@dimen/end_guest_button_border_size"
+ android:color="?android:attr/colorControlHighlight" />
+ <corners android:radius="@dimen/end_guest_button_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/kg_bg_avatar.xml b/packages/SystemUI/res/drawable/kg_bg_avatar.xml
new file mode 100644
index 000000000000..addb3f7508f5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/kg_bg_avatar.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="100"
+ android:viewportHeight="100">
+
+ <path
+ android:fillColor="@color/kg_user_switcher_avatar_background"
+ android:pathData="M50,50m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0"/>
+
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 416ee8147e33..2789ed125b09 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -43,17 +43,12 @@
<include layout="@layout/system_icons" />
</FrameLayout>
- <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
- android:layout_width="@dimen/multi_user_switch_width_keyguard"
- android:layout_height="match_parent"
- android:background="@drawable/ripple_drawable"
- android:layout_marginEnd="@dimen/multi_user_switch_keyguard_margin">
- <ImageView android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_keyguard_size"
- android:layout_height="@dimen/multi_user_avatar_keyguard_size"
- android:layout_gravity="center"
- android:scaleType="centerInside"/>
- </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+ <ImageView android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_height="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside"/>
</LinearLayout>
<Space
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
index 983ba6d5e240..253c03e9effb 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
@@ -14,10 +14,50 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<view xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.systemui.statusbar.policy.KeyguardUserSwitcher$Container"
- android:visibility="gone"
- android:layout_height="match_parent"
- android:layout_width="match_parent">
- <!-- KeyguardUserSwitcher loads keyguard_user_switcher_inner.xml here -->
-</view> \ No newline at end of file
+<!-- This is a view that shows a user switcher in Keyguard. -->
+<com.android.systemui.statusbar.policy.KeyguardUserSwitcherView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_user_switcher_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="end">
+
+ <com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView
+ android:id="@+id/keyguard_user_switcher_list"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="top|end"
+ android:gravity="end" />
+
+ <LinearLayout
+ android:id="@+id/end_guest_button"
+ android:layout_height="@dimen/end_guest_button_layout_height"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/end_guest_button_margin_bottom"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:paddingLeft="@dimen/end_guest_button_padding_horizontal"
+ android:paddingRight="@dimen/end_guest_button_padding_horizontal"
+ android:background="@drawable/end_guest_button_background"
+ android:visibility="gone">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:src="@drawable/ic_exit_to_app"
+ android:background="@android:color/transparent"
+ android:color="?attr/wallpaperTextColor" />
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:textColor="?attr/wallpaperTextColor"
+ android:textSize="13sp"
+ android:text="@string/guest_exit_button" />
+ </LinearLayout>
+
+</com.android.systemui.statusbar.policy.KeyguardUserSwitcherView>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index 1cd1a04ab462..aaa372a5be6e 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -19,29 +19,30 @@
<!-- LinearLayout -->
<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_marginEnd="8dp"
- android:gravity="center_vertical"
+ android:gravity="end|center_vertical"
android:clickable="true"
- android:background="@drawable/ripple_drawable"
- sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
- sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
- <TextView android:id="@+id/user_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="13dp"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
- />
- <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
- android:layout_width="@dimen/kg_framed_avatar_size"
- android:layout_height="@dimen/kg_framed_avatar_size"
- android:contentDescription="@null"
- sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
- sysui:framePadding="2.5dp"
- sysui:badgeDiameter="18dp"
- sysui:badgeMargin="1dp"
- sysui:frameColor="@color/kg_user_switcher_rounded_background_color" />
+ android:background="@drawable/kg_user_switcher_rounded_bg"
+ systemui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+ systemui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher">
+ <TextView
+ android:id="@+id/user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="20dp"
+ android:layout_marginEnd="16dp" />
+ <com.android.systemui.statusbar.phone.UserAvatarView
+ android:id="@+id/user_picture"
+ android:layout_width="@dimen/kg_framed_avatar_size"
+ android:layout_height="@dimen/kg_framed_avatar_size"
+ systemui:avatarPadding="0dp"
+ systemui:badgeDiameter="18dp"
+ systemui:badgeMargin="1dp"
+ systemui:frameWidth="0dp"
+ systemui:framePadding="0dp"
+ systemui:frameColor="@color/kg_user_avatar_frame" />
</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index d6385ffbcc0c..859d9048cee3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -31,6 +31,12 @@
android:layout_height="match_parent"
android:visibility="gone" />
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher_stub"
+ android:layout="@layout/keyguard_user_switcher"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
<include
layout="@layout/keyguard_status_view"
android:visibility="gone" />
@@ -72,12 +78,6 @@
<include layout="@layout/photo_preview_overlay" />
- <ViewStub
- android:id="@+id/keyguard_user_switcher"
- android:layout="@layout/keyguard_user_switcher"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
<include
layout="@layout/keyguard_status_bar"
android:visibility="invisible" />
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 3153d0d0123d..37ec576be4be 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -89,6 +89,8 @@
<color name="kg_user_switcher_avatar_icon_color">@android:color/background_light</color>
<!-- Icon color for selected user avatars in keyguard user switcher -->
<color name="kg_user_switcher_selected_avatar_icon_color">#202124</color>
+ <!-- Color of background circle of user avatars in keyguard user switcher -->
+ <color name="kg_user_switcher_avatar_background">#3C4043</color>
<!-- Icon color for user avatars in quick settings user switcher -->
<color name="qs_user_switcher_avatar_icon_color">@android:color/background_light</color>
<!-- Icon color for selected user avatars in quick settings user switcher -->
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 02bd60210e81..ee2b82dca811 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -23,13 +23,6 @@
<item name="numColumns">4</item>
</style>
- <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
- <item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
- <item name="android:textStyle">normal</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?attr/wallpaperTextColor</item>
- </style>
-
<style name="TextAppearance.QS.UserSwitcher">
<item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5fb6de7bb588..8bd9de919bee 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -66,9 +66,13 @@
<!-- Color for rounded background for activated user in keyguard user switcher -->
<color name="kg_user_switcher_activated_background_color">#26000000</color>
<!-- Icon color for user avatars in keyguard user switcher -->
- <color name="kg_user_switcher_avatar_icon_color">@android:color/background_light</color>
- <!-- Icon color for selected user avatars in keyguard user switcher -->
- <color name="kg_user_switcher_selected_avatar_icon_color">@android:color/background_light</color>
+ <color name="kg_user_switcher_avatar_icon_color">@color/GM2_grey_800</color>
+ <!-- Icon color for user avatars in keyguard user switcher that restricted
+ (e.g. cannot be switched to) -->
+ <color name="kg_user_switcher_restricted_avatar_icon_color">@color/GM2_grey_600</color>
+ <!-- Color of background circle of user avatars in keyguard user switcher -->
+ <color name="kg_user_switcher_avatar_background">@color/GM2_grey_300</color>
+
<!-- Icon color for user avatars in user switcher quick settings -->
<color name="qs_user_switcher_avatar_icon_color">#3C4043</color>
<!-- Icon color for selected user avatars in user switcher quick settings -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bb04c3b35628..61962256f93d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 08cd6553e252..594fbdf55d3c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -743,9 +743,6 @@
<!-- end margin for system icons if multi user switch is hidden -->
<dimen name="system_icons_switcher_hidden_expanded_margin">16dp</dimen>
- <!-- The thickness of the colored border around the current user. -->
- <dimen name="keyguard_user_switcher_border_thickness">2dp</dimen>
-
<dimen name="data_usage_graph_marker_width">4dp</dimen>
<!-- The padding bottom of the clock group when QS is expanded. -->
@@ -805,7 +802,7 @@
<!-- Size of user icon + frame in the qs user picker (incl. frame) -->
<dimen name="qs_framed_avatar_size">54dp</dimen>
<!-- Size of user icon + frame in the keyguard user picker (incl. frame) -->
- <dimen name="kg_framed_avatar_size">54dp</dimen>
+ <dimen name="kg_framed_avatar_size">32dp</dimen>
<!-- Margin on the left side of the carrier text on Keyguard -->
<dimen name="keyguard_carrier_text_margin">16dp</dimen>
@@ -1324,8 +1321,16 @@
<dimen name="screenrecord_status_icon_height">17.5dp</dimen>
<dimen name="screenrecord_status_icon_bg_radius">8dp</dimen>
+ <!-- Keyguard user switcher -->
<dimen name="kg_user_switcher_text_size">16sp</dimen>
+ <!-- End guest session button -->
+ <dimen name="end_guest_button_layout_height">32dp</dimen>
+ <dimen name="end_guest_button_padding_horizontal">16dp</dimen>
+ <dimen name="end_guest_button_margin_bottom">96dp</dimen>
+ <dimen name="end_guest_button_border_size">1dp</dimen>
+ <dimen name="end_guest_button_corner_radius">16dp</dimen>
+
<!-- Opacity at which the background for the shutdown UI will be drawn. -->
<item name="shutdown_scrim_behind_alpha" format="float" type="dimen">0.95</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index abcf4e802ab9..7c1c24bf280e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1112,6 +1112,9 @@
<!-- Name for a freshly added user [CHAR LIMIT=30] -->
<string name="user_new_user_name">New user</string>
+ <!-- Label for button that exits guest session and clears the guest user data [CHAR LIMIT=50]-->
+ <string name="guest_exit_button">End guest session</string>
+
<!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
<string name="guest_exit_guest_dialog_title">End guest session?</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7c72548a7252..85c470f8e706 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -114,12 +114,12 @@
<style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
<item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
<item name="android:textStyle">normal</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?attr/wallpaperTextColor</item>
</style>
<style name="TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
<item name="android:fontWeight">700</item>
- <item name="android:textStyle">bold</item>
</style>
<style name="TextAppearance" />
@@ -764,6 +764,7 @@
<style name="TextAppearance.PrivacyDialog">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="UdfpsProgressBarStyle"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index a5f364d30d7d..6fb6760be653 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -16,13 +16,9 @@
package com.android.keyguard;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-
import android.util.Slog;
import android.view.View;
-import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -50,13 +46,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final KeyguardClockSwitchController mKeyguardClockSwitchController;
- private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
private final NotificationIconAreaController mNotificationIconAreaController;
private final DozeParameters mDozeParameters;
+ private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
- private boolean mKeyguardStatusViewVisibilityAnimating;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
@Inject
@@ -72,11 +67,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
mKeyguardClockSwitchController = keyguardClockSwitchController;
- mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mConfigurationController = configurationController;
mNotificationIconAreaController = notificationIconAreaController;
mDozeParameters = dozeParameters;
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
+ dozeParameters);
}
@Override
@@ -144,7 +140,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
* Set keyguard status view alpha.
*/
public void setAlpha(float alpha) {
- if (!mKeyguardStatusViewVisibilityAnimating) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
mView.setAlpha(alpha);
}
}
@@ -200,7 +196,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
public void updatePosition(int x, int y, float scale, boolean animate) {
// We animate the status view visible/invisible using Y translation, so don't change it
// while the animation is running.
- if (!mKeyguardStatusViewVisibilityAnimating) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
animate);
}
@@ -230,69 +226,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
- mView.animate().cancel();
- mKeyguardStatusViewVisibilityAnimating = false;
- if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
- && statusBarState != KEYGUARD) || goingToFullShade) {
- mKeyguardStatusViewVisibilityAnimating = true;
- mView.animate()
- .alpha(0f)
- .setStartDelay(0)
- .setDuration(160)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(
- mAnimateKeyguardStatusViewGoneEndRunnable);
- if (keyguardFadingAway) {
- mView.animate()
- .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
- .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
- .start();
- }
- } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
- mView.setVisibility(View.VISIBLE);
- mKeyguardStatusViewVisibilityAnimating = true;
- mView.setAlpha(0f);
- mView.animate()
- .alpha(1f)
- .setStartDelay(0)
- .setDuration(320)
- .setInterpolator(Interpolators.ALPHA_IN)
- .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
- } else if (statusBarState == KEYGUARD) {
- if (keyguardFadingAway) {
- mKeyguardStatusViewVisibilityAnimating = true;
- mView.animate()
- .alpha(0)
- .translationYBy(-getHeight() * 0.05f)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(125)
- .setStartDelay(0)
- .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
- .start();
- } else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
- mKeyguardStatusViewVisibilityAnimating = true;
-
- mView.setVisibility(View.VISIBLE);
- mView.setAlpha(0f);
-
- float curTranslationY = mView.getTranslationY();
- mView.setTranslationY(curTranslationY - getHeight() * 0.1f);
- mView.animate()
- .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f))
- .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(1f)
- .translationY(curTranslationY)
- .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
- .start();
- } else {
- mView.setVisibility(View.VISIBLE);
- mView.setAlpha(1f);
- }
- } else {
- mView.setVisibility(View.GONE);
- mView.setAlpha(1f);
- }
+ mKeyguardVisibilityHelper.setViewVisibility(
+ statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
}
private void refreshTime() {
@@ -393,19 +328,4 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mView.updateLogoutView();
}
};
-
- private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
- mKeyguardStatusViewVisibilityAnimating = false;
- mView.setVisibility(View.INVISIBLE);
- };
-
-
- private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
- mKeyguardStatusViewVisibilityAnimating = false;
- mView.setVisibility(View.GONE);
- };
-
- private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
- mKeyguardStatusViewVisibilityAnimating = false;
- };
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
new file mode 100644
index 000000000000..724e1f660fb9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+/**
+ * Helper class for updating visibility of keyguard views based on keyguard and status bar state.
+ * This logic is shared by both the keyguard status view and the keyguard user switcher.
+ */
+public class KeyguardVisibilityHelper {
+
+ private View mView;
+ private final KeyguardStateController mKeyguardStateController;
+ private final DozeParameters mDozeParameters;
+ private boolean mKeyguardViewVisibilityAnimating;
+
+ public KeyguardVisibilityHelper(View view, KeyguardStateController keyguardStateController,
+ DozeParameters dozeParameters) {
+ mView = view;
+ mKeyguardStateController = keyguardStateController;
+ mDozeParameters = dozeParameters;
+ }
+
+ public boolean isVisibilityAnimating() {
+ return mKeyguardViewVisibilityAnimating;
+ }
+
+ /**
+ * Set the visibility of a keyguard view based on some new state.
+ */
+ public void setViewVisibility(
+ int statusBarState,
+ boolean keyguardFadingAway,
+ boolean goingToFullShade,
+ int oldStatusBarState) {
+ mView.animate().cancel();
+ mKeyguardViewVisibilityAnimating = false;
+ if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
+ && statusBarState != KEYGUARD) || goingToFullShade) {
+ mKeyguardViewVisibilityAnimating = true;
+ mView.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setDuration(160)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(
+ mAnimateKeyguardStatusViewGoneEndRunnable);
+ if (keyguardFadingAway) {
+ mView.animate()
+ .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
+ .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
+ .start();
+ }
+ } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
+ mView.setVisibility(View.VISIBLE);
+ mKeyguardViewVisibilityAnimating = true;
+ mView.setAlpha(0f);
+ mView.animate()
+ .alpha(1f)
+ .setStartDelay(0)
+ .setDuration(320)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
+ } else if (statusBarState == KEYGUARD) {
+ if (keyguardFadingAway) {
+ mKeyguardViewVisibilityAnimating = true;
+ mView.animate()
+ .alpha(0)
+ .translationYBy(-mView.getHeight() * 0.05f)
+ .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+ .setDuration(125)
+ .setStartDelay(0)
+ .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
+ .start();
+ } else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
+ mKeyguardViewVisibilityAnimating = true;
+
+ mView.setVisibility(View.VISIBLE);
+ mView.setAlpha(0f);
+
+ float curTranslationY = mView.getTranslationY();
+ mView.setTranslationY(curTranslationY - mView.getHeight() * 0.1f);
+ mView.animate()
+ .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f))
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ .translationY(curTranslationY)
+ .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
+ .start();
+ } else {
+ mView.setVisibility(View.VISIBLE);
+ mView.setAlpha(1f);
+ }
+ } else {
+ mView.setVisibility(View.GONE);
+ mView.setAlpha(1f);
+ }
+ }
+
+ private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
+ mKeyguardViewVisibilityAnimating = false;
+ mView.setVisibility(View.INVISIBLE);
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
+ mKeyguardViewVisibilityAnimating = false;
+ mView.setVisibility(View.GONE);
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
+ mKeyguardViewVisibilityAnimating = false;
+ };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java
new file mode 100644
index 000000000000..730c14dc9600
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.dagger;
+
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for helping work with KeyguardUserSwitcher and its children.
+ */
+@Subcomponent(modules = {KeyguardUserSwitcherModule.class})
+@KeyguardUserSwitcherScope
+public interface KeyguardUserSwitcherComponent {
+ /** Simple factory for {@link KeyguardUserSwitcherComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ KeyguardUserSwitcherComponent build(
+ @BindsInstance KeyguardUserSwitcherView keyguardUserSwitcherView);
+ }
+
+ /** Builds a {@link com.android.systemui.statusbar.policy.KeyguardUserSwitcherController}. */
+ KeyguardUserSwitcherController getKeyguardUserSwitcherController();
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java
new file mode 100644
index 000000000000..b9184f405bf9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.dagger;
+
+import dagger.Module;
+
+/** Dagger module for {@link KeyguardUserSwitcherComponent}. */
+@Module
+public abstract class KeyguardUserSwitcherModule {
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java
new file mode 100644
index 000000000000..864472e53ce7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singleton items within the KeyguardUserSwitcherComponent.
+ */
+@Documented
+@Retention(RUNTIME)
+@Scope
+public @interface KeyguardUserSwitcherScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 247f25e1ccea..6b300f4e07e4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -30,6 +30,7 @@ import android.service.controls.actions.CommandAction
import android.service.controls.actions.FloatAction
import android.util.Log
import android.view.HapticFeedbackConstants
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
@@ -71,7 +72,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
cvh.action(BooleanAction(templateId, !isChecked))
}, true /* blockable */))
@@ -79,7 +80,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
val blockable = cvh.usePanel()
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
if (cvh.usePanel()) {
showDialog(cvh, control.getAppIntent().getIntent())
@@ -98,13 +99,13 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.action(FloatAction(templateId, newValue))
}, false /* blockable */))
}
override fun longPress(cvh: ControlViewHolder) {
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ bouncerOrRun(createAction(cvh.cws.ci.controlId, {
// Long press snould only be called when there is valid control state, otherwise ignore
cvh.cws.control?.let {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
@@ -114,6 +115,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun runPendingAction(controlId: String) {
+ if (!keyguardStateController.isUnlocked()) return
if (pendingAction?.controlId == controlId) {
pendingAction?.invoke()
pendingAction = null
@@ -135,7 +137,8 @@ class ControlActionCoordinatorImpl @Inject constructor(
false
}
- private fun bouncerOrRun(action: Action) {
+ @VisibleForTesting
+ fun bouncerOrRun(action: Action) {
if (keyguardStateController.isShowing()) {
var closeDialog = !keyguardStateController.isUnlocked()
if (closeDialog) {
@@ -190,6 +193,10 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
}
+ @VisibleForTesting
+ fun createAction(controlId: String, f: () -> Unit, blockable: Boolean) =
+ Action(controlId, f, blockable)
+
inner class Action(val controlId: String, val f: () -> Unit, val blockable: Boolean) {
fun invoke() {
if (!blockable || shouldRunAction(controlId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
index f533cfb47076..db68d17461fa 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -28,11 +28,12 @@ import android.view.WindowManager
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
+import javax.inject.Inject
/**
* Show the controls space inside a dialog, as from the lock screen.
*/
-class ControlsDialog(
+class ControlsDialog @Inject constructor(
thisContext: Context,
val broadcastDispatcher: BroadcastDispatcher
) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 84dd25963a15..f3726a37bb65 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -16,6 +16,11 @@
package com.android.systemui.dagger;
+import android.content.Context;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.tv.TvWMComponent;
+import com.android.systemui.wmshell.TvWMShellModule;
import com.android.systemui.wmshell.WMShellModule;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.ShellInit;
@@ -34,7 +39,13 @@ import java.util.Optional;
import dagger.Subcomponent;
/**
- * Dagger Subcomponent for WindowManager.
+ * Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported
+ * from the WM component into the SysUI component (in
+ * {@link SystemUIFactory#init(Context, boolean)}), and references the specific dependencies
+ * provided by its particular device/form-factor SystemUI implementation.
+ *
+ * ie. {@link WMComponent} includes {@link WMShellModule}
+ * and {@link TvWMComponent} includes {@link TvWMShellModule}
*/
@WMSingleton
@Subcomponent(modules = {WMShellModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c55fdf4783e3..91cf7108c728 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -415,6 +415,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
@Override
public void onUserSwitching(int userId) {
+ if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
// Note that the mLockPatternUtils user has already been updated from setCurrentUser.
// We need to force a reset of the views, since lockNow (called by
// ActivityManagerService) will not reconstruct the keyguard if it is already showing.
@@ -432,6 +433,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
@Override
public void onUserSwitchComplete(int userId) {
+ if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
if (userId != UserHandle.USER_SYSTEM) {
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
// Don't try to dismiss if the user has Pin/Patter/Password set
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 76281d8c0f00..9e5b225fbefc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -30,6 +30,7 @@ import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
@@ -61,7 +62,7 @@ import dagger.Provides;
/**
* Dagger Module providing {@link StatusBar}.
*/
-@Module(subcomponents = {KeyguardStatusViewComponent.class},
+@Module(subcomponents = {KeyguardStatusViewComponent.class, KeyguardUserSwitcherComponent.class},
includes = {FalsingModule.class})
public class KeyguardModule {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f56a890c54d4..782092161418 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -40,6 +40,7 @@ import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessSlider;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
@@ -92,9 +93,10 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSlider.Factory brightnessSliderFactory,
- @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
+ @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
+ FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
- metricsLogger, uiEventLogger, qsLogger, dumpManager);
+ metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
mQsSecurityFooter = qsSecurityFooter;
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index b02799f49660..9426e7122c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -34,6 +34,8 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.util.Utils;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -63,6 +65,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
private final DumpManager mDumpManager;
+ private final FeatureFlags mFeatureFlags;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mLastOrientation;
@@ -93,11 +96,18 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private boolean mUsingHorizontalLayout;
- protected QSPanelControllerBase(T view, QSTileHost host,
+ protected QSPanelControllerBase(
+ T view,
+ QSTileHost host,
QSCustomizerController qsCustomizerController,
- @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, MediaHost mediaHost,
- MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager) {
+ @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
+ MediaHost mediaHost,
+ MetricsLogger metricsLogger,
+ UiEventLogger uiEventLogger,
+ QSLogger qsLogger,
+ DumpManager dumpManager,
+ FeatureFlags featureFlags
+ ) {
super(view);
mHost = host;
mQsCustomizerController = qsCustomizerController;
@@ -107,6 +117,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mUiEventLogger = uiEventLogger;
mQSLogger = qsLogger;
mDumpManager = dumpManager;
+ mFeatureFlags = featureFlags;
}
@Override
@@ -334,9 +345,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
}
boolean shouldUseHorizontalLayout() {
+ if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources())) {
+ return false;
+ }
return mUsingMediaPlayer && mMediaHost.getVisible()
- && getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
+ && getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
}
private void logTiles() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index a0db2000cb4d..383e932a6955 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -30,6 +30,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.FeatureFlags;
import java.util.ArrayList;
import java.util.List;
@@ -57,9 +58,11 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
+ DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
+ FeatureFlags featureFlags
+ ) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
- uiEventLogger, qsLogger, dumpManager);
+ uiEventLogger, qsLogger, dumpManager, featureFlags);
mUseSideLabels = qsLabelsFlag;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 11e6330d37f3..6983b38489f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -37,6 +37,7 @@ import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
import com.android.systemui.qs.tiles.DataSaverTile;
+import com.android.systemui.qs.tiles.DeviceControlsTile;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
@@ -89,6 +90,7 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<ReduceBrightColorsTile> mReduceBrightColorsTileProvider;
private final Provider<CameraToggleTile> mCameraToggleTileProvider;
private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider;
+ private final Provider<DeviceControlsTile> mDeviceControlsTileProvider;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -123,7 +125,8 @@ public class QSFactoryImpl implements QSFactory {
Provider<ScreenRecordTile> screenRecordTileProvider,
Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider,
Provider<CameraToggleTile> cameraToggleTileProvider,
- Provider<MicrophoneToggleTile> microphoneToggleTileProvider) {
+ Provider<MicrophoneToggleTile> microphoneToggleTileProvider,
+ Provider<DeviceControlsTile> deviceControlsTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -153,6 +156,7 @@ public class QSFactoryImpl implements QSFactory {
mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider;
mCameraToggleTileProvider = cameraToggleTileProvider;
mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
+ mDeviceControlsTileProvider = deviceControlsTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -212,6 +216,8 @@ public class QSFactoryImpl implements QSFactory {
return mCameraToggleTileProvider.get();
case "mictoggle":
return mMicrophoneToggleTileProvider.get();
+ case "controls":
+ return mDeviceControlsTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
new file mode 100644
index 000000000000..6176a5702dcf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.service.quicksettings.Tile
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.dagger.ControlsComponent
+import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.util.settings.GlobalSettings
+import java.util.concurrent.atomic.AtomicBoolean
+import javax.inject.Inject
+import javax.inject.Provider
+
+class DeviceControlsTile @Inject constructor(
+ host: QSHost,
+ @Background backgroundLooper: Looper,
+ @Main mainHandler: Handler,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger,
+ private val controlsComponent: ControlsComponent,
+ private val featureFlags: FeatureFlags,
+ private val dialogProvider: Provider<ControlsDialog>,
+ globalSettings: GlobalSettings
+) : QSTileImpl<QSTile.State>(
+ host,
+ backgroundLooper,
+ mainHandler,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+) {
+
+ companion object {
+ const val SETTINGS_FLAG = "controls_lockscreen"
+ }
+
+ private val controlsLockscreen = globalSettings.getInt(SETTINGS_FLAG, 0) != 0
+ private var hasControlsApps = AtomicBoolean(false)
+ private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
+
+ private var controlsDialog: ControlsDialog? = null
+ private val icon = ResourceIcon.get(R.drawable.ic_device_light)
+
+ private val listingCallback = object : ControlsListingController.ControlsListingCallback {
+ override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+ if (hasControlsApps.compareAndSet(serviceInfos.isEmpty(), serviceInfos.isNotEmpty())) {
+ refreshState()
+ }
+ }
+ }
+
+ init {
+ controlsComponent.getControlsListingController().ifPresent {
+ it.observe(this, listingCallback)
+ }
+ }
+
+ override fun isAvailable(): Boolean {
+ return featureFlags.isKeyguardLayoutEnabled &&
+ controlsLockscreen &&
+ controlsComponent.getControlsUiController().isPresent
+ }
+
+ override fun newTileState(): QSTile.State {
+ return QSTile.State().also {
+ it.state = Tile.STATE_UNAVAILABLE // Start unavailable matching `hasControlsApps`
+ }
+ }
+
+ override fun handleDestroy() {
+ dismissDialog()
+ super.handleDestroy()
+ }
+
+ private fun createDialog() {
+ if (controlsDialog?.isShowing != true) {
+ controlsDialog = dialogProvider.get()
+ }
+ }
+
+ private fun dismissDialog() {
+ controlsDialog?.dismiss()?.also {
+ controlsDialog = null
+ }
+ }
+
+ override fun handleClick() {
+ if (state.state != Tile.STATE_UNAVAILABLE) {
+ mUiHandler.post {
+ createDialog()
+ controlsDialog?.show(controlsComponent.getControlsUiController().get())
+ }
+ }
+ }
+
+ override fun handleUpdateState(state: QSTile.State, arg: Any?) {
+ state.label = tileLabel
+ state.secondaryLabel = ""
+ state.stateDescription = ""
+ state.contentDescription = state.label
+ state.icon = icon
+ if (hasControlsApps.get()) {
+ state.state = Tile.STATE_ACTIVE
+ if (controlsDialog == null) {
+ mUiHandler.post(this::createDialog)
+ }
+ } else {
+ state.state = Tile.STATE_UNAVAILABLE
+ dismissDialog()
+ }
+ }
+
+ override fun getMetricsCategory(): Int {
+ return 0
+ }
+
+ override fun getLongClickIntent(): Intent {
+ return intent
+ }
+
+ override fun getTileLabel(): CharSequence {
+ return mContext.getText(R.string.quick_controls_title)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 6a8c61491709..6ca550c9ddb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -41,7 +41,7 @@ public class UserDetailItemView extends LinearLayout {
protected static int layoutResId = R.layout.qs_user_detail_item;
private UserAvatarView mAvatar;
- private TextView mName;
+ protected TextView mName;
private int mActivatedStyle;
private int mRegularStyle;
private View mRestrictedPadlock;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index c8afd0b6cfe9..9383aefeb6b6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -16,17 +16,22 @@
package com.android.systemui.screenshot;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.android.systemui.R;
@@ -35,6 +40,7 @@ import com.android.systemui.R;
* cropped out.
*/
public class CropView extends View {
+ private static final String TAG = "CropView";
public enum CropBoundary {
NONE, TOP, BOTTOM
}
@@ -118,10 +124,7 @@ public class CropView extends View {
case MotionEvent.ACTION_UP:
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
// Commit the delta to the stored crop values.
- mTopCrop += mTopDelta;
- mBottomCrop += mBottomDelta;
- mTopDelta = 0;
- mBottomDelta = 0;
+ commitDeltas();
updateListener(event);
}
}
@@ -129,6 +132,42 @@ public class CropView extends View {
}
/**
+ * Animate the given boundary to the given value.
+ */
+ public void animateBoundaryTo(CropBoundary boundary, float value) {
+ if (boundary == CropBoundary.NONE) {
+ Log.w(TAG, "No boundary selected for animation");
+ return;
+ }
+ float totalDelta = (boundary == CropBoundary.TOP) ? (value - mTopCrop)
+ : (value - mBottomCrop);
+ ValueAnimator animator = new ValueAnimator();
+ animator.addUpdateListener(animation -> {
+ if (boundary == CropBoundary.TOP) {
+ mTopDelta = animation.getAnimatedFraction() * totalDelta;
+ } else {
+ mBottomDelta = animation.getAnimatedFraction() * totalDelta;
+ }
+ invalidate();
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ commitDeltas();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ commitDeltas();
+ }
+ });
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(750);
+ animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.start();
+ }
+
+ /**
* @return value [0,1] representing the position of the top crop boundary. Does not reflect
* changes from any in-progress touch input.
*/
@@ -148,6 +187,13 @@ public class CropView extends View {
mCropInteractionListener = listener;
}
+ private void commitDeltas() {
+ mTopCrop += mTopDelta;
+ mBottomCrop += mBottomDelta;
+ mTopDelta = 0;
+ mBottomDelta = 0;
+ }
+
private void updateListener(MotionEvent event) {
if (mCropInteractionListener != null) {
float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 25438a6f57ba..a78126e701a9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -277,6 +277,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
} else {
mPreview.setImageDrawable(mImageTileSet.getDrawable());
mMagnifierView.setImageTileset(mImageTileSet);
+ mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 43bb34380f80..0bfc8e5d554b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -41,7 +41,7 @@ import android.service.vr.IVrStateCallbacks;
import android.util.Log;
import android.util.MathUtils;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtilsInternal;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 57a64e440bf6..e0df4f8bfea9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -27,6 +27,7 @@ import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
/**
* Utility class to calculate the clock position and top padding of notifications on Keyguard.
@@ -55,6 +56,12 @@ public class KeyguardClockPositionAlgorithm {
private int mKeyguardStatusHeight;
/**
+ * Height of {@link KeyguardUserSwitcherListView} when it
+ * is closed and only the current user's icon is visible.
+ */
+ private int mKeyguardUserSwitcherHeight;
+
+ /**
* Preferred Y position of clock.
*/
private int mClockPreferredY;
@@ -173,17 +180,20 @@ public class KeyguardClockPositionAlgorithm {
* Sets up algorithm values.
*/
public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight,
- float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY,
- boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount,
- boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon,
- float qsExpansion, int cutoutTopInset) {
+ float panelExpansion, int parentHeight, int keyguardStatusHeight,
+ int keyguardUserSwitcherHeight, int clockPreferredY, boolean hasCustomClock,
+ boolean hasVisibleNotifs, float dark, float emptyDragAmount, boolean bypassEnabled,
+ int unlockedStackScrollerPadding, boolean showLockIcon, float qsExpansion,
+ int cutoutTopInset) {
mMinTopMargin = statusBarMinHeight + (showLockIcon
- ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon);
+ ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon)
+ + keyguardUserSwitcherHeight;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
mPanelExpansion = panelExpansion;
mHeight = parentHeight;
mKeyguardStatusHeight = keyguardStatusHeight;
+ mKeyguardUserSwitcherHeight = keyguardUserSwitcherHeight;
mClockPreferredY = clockPreferredY;
mHasCustomClock = hasCustomClock;
mHasVisibleNotifs = hasVisibleNotifs;
@@ -246,7 +256,8 @@ public class KeyguardClockPositionAlgorithm {
final int availableHeight = mMaxShadeBottom - mMinTopMargin;
final int containerCenter = mMinTopMargin + availableHeight / 2;
- float y = containerCenter - mKeyguardStatusHeight * CLOCK_HEIGHT_WEIGHT
+ float y = containerCenter
+ - (mKeyguardStatusHeight + mKeyguardUserSwitcherHeight) * CLOCK_HEIGHT_WEIGHT
- mClockNotificationsMargin - mNotificationStackHeight / 2;
if (y < mMinTopMargin) {
y = mMinTopMargin;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 5f547b5df671..33798d680d05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import android.annotation.ColorInt;
@@ -25,6 +26,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
@@ -45,18 +47,15 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -75,18 +74,16 @@ public class KeyguardStatusBarView extends RelativeLayout
private boolean mShowPercentAvailable;
private boolean mBatteryCharging;
- private boolean mKeyguardUserSwitcherShowing;
private boolean mBatteryListening;
private TextView mCarrierLabel;
- private MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
private StatusIconContainer mStatusIconContainer;
private BatteryController mBatteryController;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private UserSwitcherController mUserSwitcherController;
+ private boolean mKeyguardUserSwitcherEnabled;
+ private final UserManager mUserManager;
private int mSystemIconsSwitcherHiddenExpandedMargin;
private int mSystemIconsBaseMargin;
@@ -109,13 +106,13 @@ public class KeyguardStatusBarView extends RelativeLayout
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mUserManager = UserManager.get(getContext());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSystemIconsContainer = findViewById(R.id.system_icons_container);
- mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = findViewById(R.id.multi_user_avatar);
mCarrierLabel = findViewById(R.id.keyguard_carrier_text);
mBatteryView = mSystemIconsContainer.findViewById(R.id.battery);
@@ -124,7 +121,6 @@ public class KeyguardStatusBarView extends RelativeLayout
mStatusIconContainer = findViewById(R.id.statusIcons);
loadDimens();
- updateUserSwitcher();
mBatteryController = Dependency.get(BatteryController.class);
}
@@ -137,14 +133,6 @@ public class KeyguardStatusBarView extends RelativeLayout
R.dimen.multi_user_avatar_keyguard_size);
mMultiUserAvatar.setLayoutParams(lp);
- // Multi-user switch
- lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(
- R.dimen.multi_user_switch_width_keyguard);
- lp.setMarginEnd(getResources().getDimensionPixelSize(
- R.dimen.multi_user_switch_keyguard_margin));
- mMultiUserSwitch.setLayoutParams(lp);
-
// System icons
lp = (MarginLayoutParams) mSystemIconsContainer.getLayoutParams();
lp.setMarginStart(getResources().getDimensionPixelSize(
@@ -194,22 +182,28 @@ public class KeyguardStatusBarView extends RelativeLayout
}
private void updateVisibilities() {
- if (mMultiUserSwitch.getParent() != mStatusIconArea && !mKeyguardUserSwitcherShowing) {
- if (mMultiUserSwitch.getParent() != null) {
- getOverlay().remove(mMultiUserSwitch);
+ if (mMultiUserAvatar.getParent() != mStatusIconArea
+ && !mKeyguardUserSwitcherEnabled) {
+ if (mMultiUserAvatar.getParent() != null) {
+ getOverlay().remove(mMultiUserAvatar);
}
- mStatusIconArea.addView(mMultiUserSwitch, 0);
- } else if (mMultiUserSwitch.getParent() == mStatusIconArea && mKeyguardUserSwitcherShowing) {
- mStatusIconArea.removeView(mMultiUserSwitch);
+ mStatusIconArea.addView(mMultiUserAvatar, 0);
+ } else if (mMultiUserAvatar.getParent() == mStatusIconArea
+ && mKeyguardUserSwitcherEnabled) {
+ mStatusIconArea.removeView(mMultiUserAvatar);
}
- if (mKeyguardUserSwitcher == null) {
+ if (!mKeyguardUserSwitcherEnabled) {
// If we have no keyguard switcher, the screen width is under 600dp. In this case,
// we only show the multi-user switch if it's enabled through UserManager as well as
// by the user.
- if (mMultiUserSwitch.isMultiUserEnabled()) {
- mMultiUserSwitch.setVisibility(View.VISIBLE);
+ // TODO(b/138661450) Move IPC calls to background
+ boolean isMultiUserEnabled = whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled(
+ mContext.getResources().getBoolean(
+ R.bool.qs_show_user_switcher_for_single_user)));
+ if (isMultiUserEnabled) {
+ mMultiUserAvatar.setVisibility(View.VISIBLE);
} else {
- mMultiUserSwitch.setVisibility(View.GONE);
+ mMultiUserAvatar.setVisibility(View.GONE);
}
}
mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable);
@@ -220,11 +214,12 @@ public class KeyguardStatusBarView extends RelativeLayout
(LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
// If the avatar icon is gone, we need to have some end margin to display the system icons
// correctly.
- int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE
+ int baseMarginEnd = mMultiUserAvatar.getVisibility() == View.GONE
? mSystemIconsBaseMargin
: 0;
- int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin :
- baseMarginEnd;
+ int marginEnd =
+ mKeyguardUserSwitcherEnabled ? mSystemIconsSwitcherHiddenExpandedMargin
+ : baseMarginEnd;
marginEnd = calculateMargin(marginEnd, mPadding.second);
if (marginEnd != lp.getMarginEnd()) {
lp.setMarginEnd(marginEnd);
@@ -334,20 +329,11 @@ public class KeyguardStatusBarView extends RelativeLayout
}
}
- private void updateUserSwitcher() {
- boolean keyguardSwitcherAvailable = mKeyguardUserSwitcher != null;
- mMultiUserSwitch.setClickable(keyguardSwitcherAvailable);
- mMultiUserSwitch.setFocusable(keyguardSwitcherAvailable);
- mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable);
- }
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
UserInfoController userInfoController = Dependency.get(UserInfoController.class);
userInfoController.addCallback(this);
- mUserSwitcherController = Dependency.get(UserSwitcherController.class);
- mMultiUserSwitch.setUserSwitcherController(mUserSwitcherController);
userInfoController.reloadUserInfo();
Dependency.get(ConfigurationController.class).addCallback(this);
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons),
@@ -369,11 +355,6 @@ public class KeyguardStatusBarView extends RelativeLayout
mMultiUserAvatar.setImageDrawable(picture);
}
- /** */
- public void setQSDetailDisplayer(QSDetailDisplayer detailDisplayer) {
- mMultiUserSwitch.setQSDetailDisplayer(detailDisplayer);
- }
-
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
if (mBatteryCharging != charging) {
@@ -387,54 +368,42 @@ public class KeyguardStatusBarView extends RelativeLayout
// could not care less
}
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher);
- updateUserSwitcher();
- }
-
- public void setKeyguardUserSwitcherShowing(boolean showing, boolean animate) {
- mKeyguardUserSwitcherShowing = showing;
- if (animate) {
- animateNextLayoutChange();
- }
- updateVisibilities();
- updateLayoutConsideringCutout();
- updateSystemIconsLayoutParams();
+ public void setKeyguardUserSwitcherEnabled(boolean enabled) {
+ mKeyguardUserSwitcherEnabled = enabled;
}
private void animateNextLayoutChange() {
final int systemIconsCurrentX = mSystemIconsContainer.getLeft();
- final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == mStatusIconArea;
+ final boolean userAvatarVisible = mMultiUserAvatar.getParent() == mStatusIconArea;
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
- boolean userSwitcherHiding = userSwitcherVisible
- && mMultiUserSwitch.getParent() != mStatusIconArea;
+ boolean userAvatarHiding = userAvatarVisible
+ && mMultiUserAvatar.getParent() != mStatusIconArea;
mSystemIconsContainer.setX(systemIconsCurrentX);
mSystemIconsContainer.animate()
.translationX(0)
.setDuration(400)
- .setStartDelay(userSwitcherHiding ? 300 : 0)
+ .setStartDelay(userAvatarHiding ? 300 : 0)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.start();
- if (userSwitcherHiding) {
- getOverlay().add(mMultiUserSwitch);
- mMultiUserSwitch.animate()
+ if (userAvatarHiding) {
+ getOverlay().add(mMultiUserAvatar);
+ mMultiUserAvatar.animate()
.alpha(0f)
.setDuration(300)
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(() -> {
- mMultiUserSwitch.setAlpha(1f);
- getOverlay().remove(mMultiUserSwitch);
+ mMultiUserAvatar.setAlpha(1f);
+ getOverlay().remove(mMultiUserAvatar);
})
.start();
} else {
- mMultiUserSwitch.setAlpha(0f);
- mMultiUserSwitch.animate()
+ mMultiUserAvatar.setAlpha(0f);
+ mMultiUserAvatar.animate()
.alpha(1f)
.setDuration(300)
.setStartDelay(200)
@@ -452,8 +421,8 @@ public class KeyguardStatusBarView extends RelativeLayout
if (visibility != View.VISIBLE) {
mSystemIconsContainer.animate().cancel();
mSystemIconsContainer.setTranslationX(0);
- mMultiUserSwitch.animate().cancel();
- mMultiUserSwitch.setAlpha(1f);
+ mMultiUserAvatar.animate().cancel();
+ mMultiUserAvatar.setAlpha(1f);
} else {
updateVisibilities();
updateSystemIconsLayoutParams();
@@ -523,9 +492,9 @@ public class KeyguardStatusBarView extends RelativeLayout
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardStatusBarView:");
pw.println(" mBatteryCharging: " + mBatteryCharging);
- pw.println(" mKeyguardUserSwitcherShowing: " + mKeyguardUserSwitcherShowing);
pw.println(" mBatteryListening: " + mBatteryListening);
pw.println(" mLayoutState: " + mLayoutState);
+ pw.println(" mKeyguardUserSwitcherEnabled: " + mKeyguardUserSwitcherEnabled);
if (mBatteryView != null) {
mBatteryView.dump(fd, pw, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 480d3f42ae77..d9cb9ce21330 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -35,7 +35,6 @@ import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSDetailDisplayer;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserSwitcherController;
/**
@@ -44,8 +43,6 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
public class MultiUserSwitch extends FrameLayout implements View.OnClickListener {
protected QSDetailDisplayer mQSDetailDisplayer;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private boolean mKeyguardMode;
private UserSwitcherController.BaseUserAdapter mUserListener;
final UserManager mUserManager;
@@ -85,15 +82,6 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener
refreshContentDescription();
}
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- }
-
- public void setKeyguardMode(boolean keyguardShowing) {
- mKeyguardMode = keyguardShowing;
- registerListener();
- }
-
public boolean isMultiUserEnabled() {
// TODO(b/138661450) Move IPC calls to background
return whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled(
@@ -123,11 +111,7 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener
@Override
public void onClick(View v) {
- if (mKeyguardMode) {
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.show(true /* animate */);
- }
- } else if (mQSDetailDisplayer != null && mUserSwitcherController != null) {
+ if (mQSDetailDisplayer != null && mUserSwitcherController != null) {
View center = getChildCount() > 0 ? getChildAt(0) : this;
int[] tmpInt = new int[2];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e0ef3b6483a5..3b09eda4003e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -48,6 +48,7 @@ import android.hardware.biometrics.BiometricSourceType;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.UserManager;
import android.util.Log;
import android.util.MathUtils;
import android.view.DisplayCutout;
@@ -57,6 +58,7 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
@@ -76,6 +78,7 @@ import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -95,7 +98,6 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
@@ -132,8 +134,10 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUi
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Utils;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.FileDescriptor;
@@ -283,6 +287,22 @@ public class NotificationPanelViewController extends PanelViewController {
}
};
+ final KeyguardUserSwitcherController.KeyguardUserSwitcherListener
+ mKeyguardUserSwitcherListener =
+ new KeyguardUserSwitcherController.KeyguardUserSwitcherListener() {
+ @Override
+ public void onKeyguardUserSwitcherChanged(boolean open) {
+ if (mKeyguardUserSwitcherController != null
+ && mKeyguardUserSwitcherController.isSimpleUserSwitcher()) {
+ return;
+ }
+
+ updateUserSwitcherVisibility(open
+ && mKeyguardStateController.isShowing()
+ && !mKeyguardStateController.isKeyguardFadingAway());
+ }
+ };
+
private final LayoutInflater mLayoutInflater;
private final PowerManager mPowerManager;
private final AccessibilityManager mAccessibilityManager;
@@ -295,7 +315,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final MediaHierarchyManager mMediaHierarchyManager;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final QSDetailDisplayer mQSDetailDisplayer;
+ private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
private final ControlsComponent mControlsComponent;
@@ -307,7 +327,7 @@ public class NotificationPanelViewController extends PanelViewController {
private int mMaxAllowedKeyguardNotifications;
private KeyguardAffordanceHelper mAffordanceHelper;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
+ private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarView mKeyguardStatusBar;
private ViewGroup mBigClockContainer;
private QS mQs;
@@ -332,6 +352,7 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mQsExpandedWhenExpandingStarted;
private boolean mQsFullyExpanded;
private boolean mKeyguardShowing;
+ private boolean mKeyguardUserSwitcherEnabled;
private boolean mDozing;
private boolean mDozingOnDown;
private int mBarState;
@@ -464,6 +485,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final UserManager mUserManager;
private final ShadeController mShadeController;
private final MediaDataManager mMediaDataManager;
private int mDisplayId;
@@ -556,11 +578,12 @@ public class NotificationPanelViewController extends PanelViewController {
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
+ KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController,
AuthController authController,
- QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
+ UserManager userManager,
MediaDataManager mediaDataManager,
AmbientState ambientState,
FeatureFlags featureFlags,
@@ -581,8 +604,10 @@ public class NotificationPanelViewController extends PanelViewController {
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- mQSDetailDisplayer = qsDetailDisplayer;
mFeatureFlags = featureFlags;
+ mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
+ mKeyguardUserSwitcherEnabled = mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher);
mView.setWillNotDraw(!DEBUG);
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
@@ -598,6 +623,7 @@ public class NotificationPanelViewController extends PanelViewController {
mDozeParameters = dozeParameters;
mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
+ mUserManager = userManager;
mMediaDataManager = mediaDataManager;
mControlsComponent = controlsComponent;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
@@ -660,9 +686,17 @@ public class NotificationPanelViewController extends PanelViewController {
private void onFinishInflate() {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
- mKeyguardStatusBar.setQSDetailDisplayer(mQSDetailDisplayer);
mBigClockContainer = mView.findViewById(R.id.big_clock_container);
- updateViewControllers(mView.findViewById(R.id.keyguard_status_view));
+
+ KeyguardUserSwitcherView keyguardUserSwitcherView = null;
+
+ if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) {
+ ViewStub userSwitcherStub = mView.findViewById(R.id.keyguard_user_switcher_stub);
+ keyguardUserSwitcherView = (KeyguardUserSwitcherView) userSwitcherStub.inflate();
+ }
+
+ updateViewControllers(mView.findViewById(R.id.keyguard_status_view),
+ keyguardUserSwitcherView);
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
R.id.notification_stack_scroller);
@@ -735,7 +769,8 @@ public class NotificationPanelViewController extends PanelViewController {
R.dimen.heads_up_status_bar_padding);
}
- private void updateViewControllers(KeyguardStatusView keyguardStatusView) {
+ private void updateViewControllers(KeyguardStatusView keyguardStatusView,
+ KeyguardUserSwitcherView keyguardUserSwitcherView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
@@ -746,6 +781,28 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardClockSwitchController keyguardClockSwitchController =
statusViewComponent.getKeyguardClockSwitchController();
keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
+
+ if (mKeyguardUserSwitcherController != null) {
+ // Try to close the switcher so that callbacks are triggered if necessary.
+ // Otherwise, NPV can get into a state where some of the views are still hidden
+ mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
+ mKeyguardUserSwitcherController.removeCallback();
+ }
+
+ // Re-associate the KeyguardUserSwitcherController
+ if (keyguardUserSwitcherView != null) {
+ KeyguardUserSwitcherComponent userSwitcherComponent =
+ mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
+
+ mKeyguardUserSwitcherController =
+ userSwitcherComponent.getKeyguardUserSwitcherController();
+ mKeyguardUserSwitcherController.setCallback(mKeyguardUserSwitcherListener);
+ mKeyguardUserSwitcherController.init();
+ mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
+ } else {
+ mKeyguardUserSwitcherController = null;
+ mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(false);
+ }
}
/**
@@ -783,18 +840,13 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.setLayoutParams(lp);
}
- if (shouldUseSplitNotificationShade()) {
+ if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
// In order to change the constraints at runtime, all children of the Constraint Layout
// must have ids.
ensureAllViewsHaveIds(mNotificationContainerParent);
}
}
- private boolean shouldUseSplitNotificationShade() {
- return mFeatureFlags.isTwoColumnNotificationShadeEnabled()
- && mResources.getBoolean(R.bool.config_use_split_notification_shade);
- }
-
private static void ensureAllViewsHaveIds(ViewGroup parentView) {
for (int i = 0; i < parentView.getChildCount(); i++) {
View childView = parentView.getChildAt(i);
@@ -805,6 +857,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void reInflateViews() {
+ if (DEBUG) Log.d(TAG, "reInflateViews");
// Re-inflate the status view group.
KeyguardStatusView keyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
int index = mView.indexOfChild(keyguardStatusView);
@@ -813,8 +866,27 @@ public class NotificationPanelViewController extends PanelViewController {
R.layout.keyguard_status_view, mView, false);
mView.addView(keyguardStatusView, index);
+ // Re-inflate the keyguard user switcher group.
+ boolean showUserSwitcher =
+ mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled();
+ KeyguardUserSwitcherView keyguardUserSwitcherView = mView.findViewById(
+ R.id.keyguard_user_switcher_view);
+ if (keyguardUserSwitcherView != null) {
+ index = mView.indexOfChild(keyguardUserSwitcherView);
+ mView.removeView(keyguardUserSwitcherView);
+ if (showUserSwitcher) {
+ keyguardUserSwitcherView = (KeyguardUserSwitcherView) mLayoutInflater.inflate(
+ R.layout.keyguard_user_switcher, mView, false);
+ mView.addView(keyguardUserSwitcherView, index);
+ }
+ } else if (showUserSwitcher) {
+ // It's possible the user switcher was never inflated if the configuration changed
+ ViewStub userSwitcherStub = mView.findViewById(R.id.keyguard_user_switcher_stub);
+ keyguardUserSwitcherView = (KeyguardUserSwitcherView) userSwitcherStub.inflate();
+ }
+
mBigClockContainer.removeAllViews();
- updateViewControllers(keyguardStatusView);
+ updateViewControllers(keyguardStatusView, keyguardUserSwitcherView);
// Update keyguard bottom area
index = mView.indexOfChild(mKeyguardBottomArea);
@@ -838,6 +910,13 @@ public class NotificationPanelViewController extends PanelViewController {
false,
false,
mBarState);
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
+ mBarState,
+ false,
+ false,
+ mBarState);
+ }
setKeyguardBottomAreaVisibility(mBarState, false);
}
@@ -942,6 +1021,8 @@ public class NotificationPanelViewController extends PanelViewController {
? mKeyguardStatusViewController.getHeight()
: (int) (mKeyguardStatusViewController.getHeight()
- mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
+ mKeyguardUserSwitcherController == null
+ ? 0 : mKeyguardUserSwitcherController.getUserIconHeight(),
clockPreferredY, hasCustomClock(),
hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
@@ -952,6 +1033,13 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.clockY
+ - mKeyguardUserSwitcherController.getUserIconHeight(),
+ animateClock);
+ }
updateNotificationTranslucency();
updateClock();
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
@@ -1092,6 +1180,9 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateClock() {
mKeyguardStatusViewController.setAlpha(mClockPositionResult.clockAlpha);
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.setAlpha(mClockPositionResult.clockAlpha);
+ }
}
public void animateToFullShade(long delay) {
@@ -1773,8 +1864,9 @@ public class NotificationPanelViewController extends PanelViewController {
mBarState != KEYGUARD && (!mQsExpanded
|| mQsExpansionFromOverscroll));
- if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
- mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
+ if (mKeyguardUserSwitcherController != null && mQsExpanded
+ && !mStackScrollerOverscrolling) {
+ mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true);
}
if (mQs == null) return;
mQs.setExpanded(mQsExpanded);
@@ -2608,10 +2700,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- }
-
public void onScreenTurningOn() {
mKeyguardStatusViewController.dozeTimeTick();
}
@@ -3078,6 +3166,13 @@ public class NotificationPanelViewController extends PanelViewController {
true /* keyguardFadingAway */,
false /* goingToFullShade */,
mBarState);
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
+ mBarState,
+ true /* keyguardFadingAway */,
+ false /* goingToFullShade */,
+ mBarState);
+ }
}
/**
@@ -3320,6 +3415,44 @@ public class NotificationPanelViewController extends PanelViewController {
return mNotificationStackScrollLayoutController;
}
+ /**
+ * Close the keyguard user switcher if it is open and capable of closing.
+ *
+ * Has no effect if user switcher isn't supported, if the user switcher is already closed, or
+ * if the user switcher uses "simple" mode. The simple user switcher cannot be closed.
+ *
+ * @return true if the keyguard user switcher was open, and is now closed
+ */
+ public boolean closeUserSwitcherIfOpen() {
+ if (mKeyguardUserSwitcherController != null) {
+ return mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(
+ true /* animate */);
+ }
+ return false;
+ }
+
+ private void updateUserSwitcherVisibility(boolean open) {
+ if (open) {
+ animateKeyguardStatusBarOut();
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ mBarState,
+ true /* keyguardFadingAway */,
+ true /* goingToFullShade */,
+ mBarState);
+ setKeyguardBottomAreaVisibility(mBarState, true);
+ mNotificationContainerParent.setVisibility(View.GONE);
+ } else {
+ animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ StatusBarState.KEYGUARD,
+ false,
+ false,
+ StatusBarState.SHADE_LOCKED);
+ setKeyguardBottomAreaVisibility(mBarState, false);
+ mNotificationContainerParent.setVisibility(View.VISIBLE);
+ }
+ }
+
private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
@Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
@@ -3620,6 +3753,7 @@ public class NotificationPanelViewController extends PanelViewController {
private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
@Override
public void onThemeChanged() {
+ if (DEBUG) Log.d(TAG, "onThemeChanged");
final int themeResId = mView.getContext().getThemeResId();
if (mThemeResId == themeResId) {
return;
@@ -3631,11 +3765,15 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onOverlayChanged() {
+ if (DEBUG) Log.d(TAG, "onOverlayChanged");
reInflateViews();
}
@Override
- public void onUiModeChanged() {}
+ public void onDensityOrFontScaleChanged() {
+ if (DEBUG) Log.d(TAG, "onDensityOrFontScaleChanged");
+ reInflateViews();
+ }
}
private class StatusBarStateListener implements StateListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index b36740620d08..e394ebc65a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -22,8 +22,6 @@ import android.content.res.Configuration;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewStub;
-import android.view.ViewStub.OnInflateListener;
import android.view.WindowInsets;
import android.widget.FrameLayout;
@@ -44,14 +42,11 @@ import java.util.Comparator;
* The container with notification stack scroller and quick settings inside.
*/
public class NotificationsQuickSettingsContainer extends ConstraintLayout
- implements OnInflateListener, FragmentListener,
- AboveShelfObserver.HasViewAboveShelfChangedListener {
+ implements FragmentListener, AboveShelfObserver.HasViewAboveShelfChangedListener {
private FrameLayout mQsFrame;
- private View mUserSwitcher;
private NotificationStackScrollLayout mStackScroller;
private View mKeyguardStatusBar;
- private boolean mInflated;
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
@@ -73,9 +68,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
mStackScroller = findViewById(R.id.notification_stack_scroller);
mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
- ViewStub userSwitcher = findViewById(R.id.keyguard_user_switcher);
- userSwitcher.setOnInflateListener(this);
- mUserSwitcher = userSwitcher;
}
@Override
@@ -119,10 +111,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
// touches first but the panel gets drawn above.
mDrawingOrderedChildren.clear();
mLayoutDrawingOrder.clear();
- if (mInflated && mUserSwitcher.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mUserSwitcher);
- mLayoutDrawingOrder.add(mUserSwitcher);
- }
if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) {
mDrawingOrderedChildren.add(mKeyguardStatusBar);
mLayoutDrawingOrder.add(mKeyguardStatusBar);
@@ -158,14 +146,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
}
@Override
- public void onInflate(ViewStub stub, View inflated) {
- if (stub == mUserSwitcher) {
- mUserSwitcher = inflated;
- mInflated = true;
- }
- }
-
- @Override
public void onFragmentViewCreated(String tag, Fragment fragment) {
QS container = (QS) fragment;
container.setContainer(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1e19beeff730..041a97e1d404 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -89,7 +89,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.UserManager;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
@@ -226,7 +225,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
@@ -623,7 +621,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
private final UserSwitcherController mUserSwitcherController;
private final NetworkController mNetworkController;
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -1212,9 +1209,6 @@ public class StatusBar extends SystemUI implements DemoMode,
});
mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
- if (UserManager.get(mContext).isUserSwitcherEnabled()) {
- createUserSwitcher();
- }
// Set up the quick settings tile panel
final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
@@ -1441,9 +1435,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// TODO: Bring these out of StatusBar.
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
mUserSwitcherController.onDensityOrFontScaleChanged();
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
- }
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
mHeadsUpManager.onDensityOrFontScaleChanged();
}
@@ -1477,13 +1468,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- protected void createUserSwitcher() {
- mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
- mNotificationShadeWindowView.findViewById(R.id.keyguard_user_switcher),
- mNotificationShadeWindowView.findViewById(R.id.keyguard_header),
- mNotificationPanelViewController);
- }
-
private void inflateStatusBarWindow() {
mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
@@ -3266,7 +3250,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
- } else if (!mPulseExpansionHandler.isWakingToShadeLocked()){
+ } else if (!mPulseExpansionHandler.isWakingToShadeLocked()) {
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
@@ -3565,15 +3549,15 @@ public class StatusBar extends SystemUI implements DemoMode,
}
return true;
}
+ if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
+ return true;
+ }
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
}
return true;
}
- if (mKeyguardUserSwitcher != null && mKeyguardUserSwitcher.hideIfNotSimple(true)) {
- return true;
- }
return false;
}
@@ -3622,20 +3606,8 @@ public class StatusBar extends SystemUI implements DemoMode,
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
Trace.beginSection("StatusBar#updateKeyguardState");
- if (mState == StatusBarState.KEYGUARD) {
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.setKeyguard(true,
- mStatusBarStateController.fromShadeLocked());
- }
- if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables();
- } else {
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.setKeyguard(false,
- mStatusBarStateController.goingToFullShade() ||
- mState == StatusBarState.SHADE_LOCKED ||
- mStatusBarStateController.fromShadeLocked());
- }
-
+ if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
+ mStatusBarView.removePendingHideExpandedRunnables();
}
updateDozingState();
checkBarModes();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
index 07433e13104c..0649478a42aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
@@ -17,8 +17,15 @@
package com.android.systemui.statusbar.policy;
import android.content.Context;
+import android.graphics.Color;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import androidx.core.graphics.ColorUtils;
+
+import com.android.keyguard.KeyguardConstants;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.qs.tiles.UserDetailItemView;
@@ -27,6 +34,14 @@ import com.android.systemui.qs.tiles.UserDetailItemView;
*/
public class KeyguardUserDetailItemView extends UserDetailItemView {
+ private static final String TAG = "KeyguardUserDetailItemView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private static final int ANIMATION_DURATION_FADE_NAME = 240;
+
+ private float mDarkAmount;
+ private int mTextColor;
+
public KeyguardUserDetailItemView(Context context) {
this(context, null);
}
@@ -48,4 +63,89 @@ public class KeyguardUserDetailItemView extends UserDetailItemView {
protected int getFontSizeDimen() {
return R.dimen.kg_user_switcher_text_size;
}
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTextColor = mName.getCurrentTextColor();
+ updateDark();
+ }
+
+ /**
+ * Update visibility of this view.
+ *
+ * @param showItem If true, this item is visible on the screen to the user. Generally this
+ * means that the item would be clickable. If false, item visibility will be
+ * set to GONE and hidden entirely.
+ * @param showTextName Whether or not the name should be shown next to the icon. If false,
+ * only the icon is shown.
+ * @param animate Whether the transition should be animated. Note, this only applies to
+ * animating the text name. The item itself will not animate (i.e. fade in/out).
+ * Instead, we delegate that to the parent view.
+ */
+ void updateVisibilities(boolean showItem, boolean showTextName, boolean animate) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("updateVisibilities itemIsShown=%b nameIsShown=%b animate=%b",
+ showItem, showTextName, animate));
+ }
+
+ getBackground().setAlpha((showItem && showTextName) ? 255 : 0);
+
+ if (showItem) {
+ if (showTextName) {
+ mName.setVisibility(View.VISIBLE);
+ if (animate) {
+ mName.setAlpha(0f);
+ mName.animate()
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_FADE_NAME)
+ .setInterpolator(Interpolators.ALPHA_IN);
+ } else {
+ mName.setAlpha(1f);
+ }
+ } else {
+ if (animate) {
+ mName.setVisibility(View.VISIBLE);
+ mName.setAlpha(1f);
+ mName.animate()
+ .alpha(0f)
+ .setDuration(ANIMATION_DURATION_FADE_NAME)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(() -> {
+ mName.setVisibility(View.GONE);
+ mName.setAlpha(1f);
+ });
+ } else {
+ mName.setVisibility(View.GONE);
+ mName.setAlpha(1f);
+ }
+ }
+ setVisibility(View.VISIBLE);
+ setAlpha(1f);
+ } else {
+ // If item isn't shown, don't animate. The parent class will animate the view instead
+ setVisibility(View.GONE);
+ setAlpha(1f);
+ mName.setVisibility(showTextName ? View.VISIBLE : View.GONE);
+ mName.setAlpha(1f);
+ }
+ }
+
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ public void setDarkAmount(float darkAmount) {
+ if (mDarkAmount == darkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ updateDark();
+ }
+
+ private void updateDark() {
+ final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ mName.setTextColor(blendedTextColor);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
deleted file mode 100644
index 90f557753132..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2014 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.policy;
-
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.widget.FrameLayout;
-
-import com.android.settingslib.animation.AppearAnimationUtils;
-import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.qs.tiles.UserDetailItemView;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-
-import java.util.ArrayList;
-
-/**
- * Manages the user switcher on the Keyguard.
- */
-public class KeyguardUserSwitcher {
-
- private static final String TAG = "KeyguardUserSwitcher";
- private static final boolean ALWAYS_ON = false;
-
- private final Container mUserSwitcherContainer;
- private final KeyguardStatusBarView mStatusBarView;
- private final KeyguardUserAdapter mAdapter;
- private final AppearAnimationUtils mAppearAnimationUtils;
- private final KeyguardUserSwitcherScrim mBackground;
-
- private ViewGroup mUserSwitcher;
- private ObjectAnimator mBgAnimator;
- private UserSwitcherController mUserSwitcherController;
- private boolean mAnimating;
-
- public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
- KeyguardStatusBarView statusBarView,
- NotificationPanelViewController panelViewController) {
- boolean keyguardUserSwitcherEnabled =
- context.getResources().getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher) || ALWAYS_ON;
- UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class);
- if (userSwitcherController != null && keyguardUserSwitcherEnabled) {
- mUserSwitcherContainer = (Container) userSwitcher.inflate();
- mBackground = new KeyguardUserSwitcherScrim(context);
- reinflateViews();
- mStatusBarView = statusBarView;
- mStatusBarView.setKeyguardUserSwitcher(this);
- panelViewController.setKeyguardUserSwitcher(this);
- mAdapter = new KeyguardUserAdapter(context, userSwitcherController, this);
- mAdapter.registerDataSetObserver(mDataSetObserver);
- mUserSwitcherController = userSwitcherController;
- mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
- Interpolators.FAST_OUT_SLOW_IN);
- mUserSwitcherContainer.setKeyguardUserSwitcher(this);
- } else {
- mUserSwitcherContainer = null;
- mStatusBarView = null;
- mAdapter = null;
- mAppearAnimationUtils = null;
- mBackground = null;
- }
- }
-
- private void reinflateViews() {
- if (mUserSwitcher != null) {
- mUserSwitcher.setBackground(null);
- mUserSwitcher.removeOnLayoutChangeListener(mBackground);
- }
- mUserSwitcherContainer.removeAllViews();
-
- LayoutInflater.from(mUserSwitcherContainer.getContext())
- .inflate(R.layout.keyguard_user_switcher_inner, mUserSwitcherContainer);
-
- mUserSwitcher = (ViewGroup) mUserSwitcherContainer.findViewById(
- R.id.keyguard_user_switcher_inner);
- mUserSwitcher.addOnLayoutChangeListener(mBackground);
- mUserSwitcher.setBackground(mBackground);
- }
-
- public void setKeyguard(boolean keyguard, boolean animate) {
- if (mUserSwitcher != null) {
- if (keyguard && shouldExpandByDefault()) {
- show(animate);
- } else {
- hide(animate);
- }
- }
- }
-
- /**
- * @return true if the user switcher should be expanded by default on the lock screen.
- * @see android.os.UserManager#isUserSwitcherEnabled()
- */
- private boolean shouldExpandByDefault() {
- return (mUserSwitcherController != null) && mUserSwitcherController.isSimpleUserSwitcher();
- }
-
- public void show(boolean animate) {
- if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() != View.VISIBLE) {
- cancelAnimations();
- mAdapter.refresh();
- mUserSwitcherContainer.setVisibility(View.VISIBLE);
- mStatusBarView.setKeyguardUserSwitcherShowing(true, animate);
- if (animate) {
- startAppearAnimation();
- }
- }
- }
-
- private boolean hide(boolean animate) {
- if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() == View.VISIBLE) {
- cancelAnimations();
- if (animate) {
- startDisappearAnimation();
- } else {
- mUserSwitcherContainer.setVisibility(View.GONE);
- }
- mStatusBarView.setKeyguardUserSwitcherShowing(false, animate);
- return true;
- }
- return false;
- }
-
- private void cancelAnimations() {
- int count = mUserSwitcher.getChildCount();
- for (int i = 0; i < count; i++) {
- mUserSwitcher.getChildAt(i).animate().cancel();
- }
- if (mBgAnimator != null) {
- mBgAnimator.cancel();
- }
- mUserSwitcher.animate().cancel();
- mAnimating = false;
- }
-
- private void startAppearAnimation() {
- int count = mUserSwitcher.getChildCount();
- View[] objects = new View[count];
- for (int i = 0; i < count; i++) {
- objects[i] = mUserSwitcher.getChildAt(i);
- }
- mUserSwitcher.setClipChildren(false);
- mUserSwitcher.setClipToPadding(false);
- mAppearAnimationUtils.startAnimation(objects, new Runnable() {
- @Override
- public void run() {
- mUserSwitcher.setClipChildren(true);
- mUserSwitcher.setClipToPadding(true);
- }
- });
- mAnimating = true;
- mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
- mBgAnimator.setDuration(400);
- mBgAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mBgAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBgAnimator = null;
- mAnimating = false;
- }
- });
- mBgAnimator.start();
- }
-
- private void startDisappearAnimation() {
- mAnimating = true;
- mUserSwitcher.animate()
- .alpha(0f)
- .setDuration(300)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mUserSwitcherContainer.setVisibility(View.GONE);
- mUserSwitcher.setAlpha(1f);
- mAnimating = false;
- }
- });
- }
-
- private void refresh() {
- final int childCount = mUserSwitcher.getChildCount();
- final int adapterCount = mAdapter.getCount();
- final int N = Math.max(childCount, adapterCount);
- for (int i = 0; i < N; i++) {
- if (i < adapterCount) {
- View oldView = null;
- if (i < childCount) {
- oldView = mUserSwitcher.getChildAt(i);
- }
- View newView = mAdapter.getView(i, oldView, mUserSwitcher);
- if (oldView == null) {
- // We ran out of existing views. Add it at the end.
- mUserSwitcher.addView(newView);
- } else if (oldView != newView) {
- // We couldn't rebind the view. Replace it.
- mUserSwitcher.removeViewAt(i);
- mUserSwitcher.addView(newView, i);
- }
- } else {
- int lastIndex = mUserSwitcher.getChildCount() - 1;
- mUserSwitcher.removeViewAt(lastIndex);
- }
- }
- }
-
- public boolean hideIfNotSimple(boolean animate) {
- if (mUserSwitcherContainer != null && !mUserSwitcherController.isSimpleUserSwitcher()) {
- return hide(animate);
- }
- return false;
- }
-
- boolean isAnimating() {
- return mAnimating;
- }
-
- public final DataSetObserver mDataSetObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- refresh();
- }
- };
-
- public void onDensityOrFontScaleChanged() {
- if (mUserSwitcherContainer != null) {
- reinflateViews();
- refresh();
- }
- }
-
- static class KeyguardUserAdapter extends
- UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
-
- private Context mContext;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private View mCurrentUserView;
- // List of users where the first entry is always the current user
- private ArrayList<UserSwitcherController.UserRecord> mUsersOrdered = new ArrayList<>();
-
- KeyguardUserAdapter(Context context, UserSwitcherController controller,
- KeyguardUserSwitcher kgu) {
- super(controller);
- mContext = context;
- mKeyguardUserSwitcher = kgu;
- }
-
- @Override
- public void notifyDataSetChanged() {
- refreshUserOrder();
- super.notifyDataSetChanged();
- }
-
- void refreshUserOrder() {
- ArrayList<UserSwitcherController.UserRecord> users = super.getUsers();
- mUsersOrdered = new ArrayList<>(users.size());
- for (int i = 0; i < users.size(); i++) {
- UserSwitcherController.UserRecord record = users.get(i);
- if (record.isCurrent) {
- mUsersOrdered.add(0, record);
- } else {
- mUsersOrdered.add(record);
- }
- }
- }
-
- @Override
- protected ArrayList<UserSwitcherController.UserRecord> getUsers() {
- return mUsersOrdered;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- UserSwitcherController.UserRecord item = getItem(position);
- return createUserDetailItemView(convertView, parent, item);
- }
-
- KeyguardUserDetailItemView convertOrInflate(View convertView, ViewGroup parent) {
- if (!(convertView instanceof KeyguardUserDetailItemView)
- || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
- convertView = LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_user_switcher_item, parent, false);
- }
- return (KeyguardUserDetailItemView) convertView;
- }
-
- UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
- UserSwitcherController.UserRecord item) {
- KeyguardUserDetailItemView v = convertOrInflate(convertView, parent);
- if (!item.isCurrent || item.isGuest) {
- v.setOnClickListener(this);
- } else {
- v.setOnClickListener(null);
- v.setClickable(false);
- }
-
- String name = getName(mContext, item);
- if (item.picture == null) {
- v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
- } else {
- int avatarSize =
- (int) mContext.getResources().getDimension(R.dimen.kg_framed_avatar_size);
- Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
- drawable.setColorFilter(
- item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
- v.bind(name, drawable, item.info.id);
- }
- v.setActivated(item.isCurrent);
- v.setDisabledByAdmin(item.isDisabledByAdmin);
- v.setEnabled(item.isSwitchToEnabled);
- v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
-
- if (item.isCurrent) {
- mCurrentUserView = v;
- }
- v.setTag(item);
- return v;
- }
-
- private static Drawable getDrawable(Context context,
- UserSwitcherController.UserRecord item) {
- Drawable drawable = getIconDrawable(context, item);
- int iconColorRes;
- if (item.isCurrent) {
- iconColorRes = R.color.kg_user_switcher_selected_avatar_icon_color;
- } else if (!item.isSwitchToEnabled) {
- iconColorRes = R.color.GM2_grey_600;
- } else {
- iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
- }
- drawable.setTint(context.getResources().getColor(iconColorRes, context.getTheme()));
-
- if (item.isCurrent) {
- Drawable bg = context.getDrawable(R.drawable.bg_avatar_selected);
- drawable = new LayerDrawable(new Drawable[]{bg, drawable});
- }
-
- return drawable;
- }
-
- @Override
- public void onClick(View v) {
- UserSwitcherController.UserRecord user = (UserSwitcherController.UserRecord) v.getTag();
- if (user.isCurrent && !user.isGuest) {
- // Close the switcher if tapping the current user. Guest is excluded because
- // tapping the guest user while it's current clears the session.
- mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
- } else if (user.isSwitchToEnabled) {
- if (!user.isAddUser && !user.isRestricted && !user.isDisabledByAdmin) {
- if (mCurrentUserView != null) {
- mCurrentUserView.setActivated(false);
- }
- v.setActivated(true);
- }
- onUserListItemClicked(user);
- }
- }
- }
-
- public static class Container extends FrameLayout {
-
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
-
- public Container(Context context, AttributeSet attrs) {
- super(context, attrs);
- setClipChildren(false);
- }
-
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- // Hide switcher if it didn't handle the touch event (and let the event go through).
- if (mKeyguardUserSwitcher != null && !mKeyguardUserSwitcher.isAnimating()) {
- mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
- }
- return false;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
new file mode 100644
index 000000000000..b76e451cb681
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
+import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.keyguard.KeyguardConstants;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.KeyguardVisibilityHelper;
+import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.ViewController;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+
+/**
+ * Manages the user switcher on the Keyguard.
+ */
+@KeyguardUserSwitcherScope
+public class KeyguardUserSwitcherController extends ViewController<KeyguardUserSwitcherView> {
+
+ private static final String TAG = "KeyguardUserSwitcherController";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private static final AnimationProperties ANIMATION_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+ private final Context mContext;
+ private final UserSwitcherController mUserSwitcherController;
+ private final ScreenLifecycle mScreenLifecycle;
+ private final KeyguardUserAdapter mAdapter;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private WeakReference<KeyguardUserSwitcherListener> mKeyguardUserSwitcherCallback;
+ protected final SysuiStatusBarStateController mStatusBarStateController;
+ private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+
+ // Child views of KeyguardUserSwitcherView
+ private KeyguardUserSwitcherListView mListView;
+ private LinearLayout mEndGuestButton;
+
+ // State info for the user switcher
+ private boolean mUserSwitcherOpen;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private boolean mCurrentUserIsGuest;
+ private int mBarState;
+ private float mDarkAmount;
+
+ private final KeyguardUpdateMonitorCallback mInfoCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, String.format("onKeyguardVisibilityChanged %b", showing));
+ // Any time the keyguard is hidden, try to close the user switcher menu to
+ // restore keyguard to the default state
+ if (!showing) {
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ }
+
+ @Override
+ public void onUserSwitching(int userId) {
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ };
+
+ private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
+ @Override
+ public void onScreenTurnedOff() {
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOff");
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ };
+
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ if (DEBUG) Log.d(TAG, String.format("onStateChanged: newState=%d", newState));
+
+ boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
+ boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
+ int oldState = mBarState;
+ mBarState = newState;
+
+ if (mStatusBarStateController.goingToFullShade()
+ || mKeyguardStateController.isKeyguardFadingAway()) {
+ closeSwitcherIfOpenAndNotSimple(true);
+ }
+
+ setKeyguardUserSwitcherVisibility(
+ newState,
+ keyguardFadingAway,
+ goingToFullShade,
+ oldState);
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linearAmount, float amount) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("onDozeAmountChanged: linearAmount=%f amount=%f",
+ linearAmount, amount));
+ }
+ setDarkAmount(amount);
+ }
+ };
+
+ @Inject
+ public KeyguardUserSwitcherController(
+ KeyguardUserSwitcherView keyguardUserSwitcherView,
+ Context context,
+ @Main Resources resources,
+ LayoutInflater layoutInflater,
+ ScreenLifecycle screenLifecycle,
+ UserSwitcherController userSwitcherController,
+ KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DozeParameters dozeParameters) {
+ super(keyguardUserSwitcherView);
+ if (DEBUG) Log.d(TAG, "New KeyguardUserSwitcherController");
+ mContext = context;
+ mScreenLifecycle = screenLifecycle;
+ mUserSwitcherController = userSwitcherController;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mAdapter = new KeyguardUserAdapter(mContext, resources, layoutInflater,
+ mUserSwitcherController, this);
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
+ keyguardStateController, dozeParameters);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+
+ if (DEBUG) Log.d(TAG, "onInit");
+
+ mListView = mView.findViewById(R.id.keyguard_user_switcher_list);
+ mEndGuestButton = mView.findViewById(R.id.end_guest_button);
+
+ mEndGuestButton.setOnClickListener(v -> {
+ mUserSwitcherController.showExitGuestDialog(mCurrentUserId);
+ });
+
+ mView.setOnTouchListener((v, event) -> {
+ if (!isListAnimating()) {
+ // Hide switcher if it didn't handle the touch event (and block the event from
+ // going through).
+ return closeSwitcherIfOpenAndNotSimple(true);
+ }
+ return false;
+ });
+ }
+
+ @Override
+ protected void onViewAttached() {
+ if (DEBUG) Log.d(TAG, "onViewAttached");
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+ mDataSetObserver.onChanged();
+ mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mScreenLifecycle.addObserver(mScreenObserver);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ if (DEBUG) Log.d(TAG, "onViewDetached");
+
+ // Detaching the view will always close the switcher
+ closeSwitcherIfOpenAndNotSimple(false);
+
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ mScreenLifecycle.removeObserver(mScreenObserver);
+ }
+
+ /**
+ * See:
+ *
+ * <ul>
+ * <li>{@link com.android.internal.R.bool.config_expandLockScreenUserSwitcher}</li>
+ * <li>{@link UserSwitcherController.SIMPLE_USER_SWITCHER_GLOBAL_SETTING}</li>
+ * </ul>
+ *
+ * @return true if the user switcher should be open by default on the lock screen.
+ * @see android.os.UserManager#isUserSwitcherEnabled()
+ */
+ public boolean isSimpleUserSwitcher() {
+ return mUserSwitcherController.isSimpleUserSwitcher();
+ }
+
+ /**
+ * @param animate if the transition should be animated
+ * @return true if the switcher state changed
+ */
+ public boolean closeSwitcherIfOpenAndNotSimple(boolean animate) {
+ if (isUserSwitcherOpen() && !isSimpleUserSwitcher()) {
+ setUserSwitcherOpened(false /* open */, animate);
+ return true;
+ }
+ return false;
+ }
+
+ public final DataSetObserver mDataSetObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ refreshUserList();
+ }
+ };
+
+ void refreshUserList() {
+ final int childCount = mListView.getChildCount();
+ final int adapterCount = mAdapter.getCount();
+ final int count = Math.max(childCount, adapterCount);
+
+ if (DEBUG) {
+ Log.d(TAG, String.format("refreshUserList childCount=%d adapterCount=%d", childCount,
+ adapterCount));
+ }
+
+ boolean foundCurrentUser = false;
+ for (int i = 0; i < count; i++) {
+ if (i < adapterCount) {
+ View oldView = null;
+ if (i < childCount) {
+ oldView = mListView.getChildAt(i);
+ }
+ KeyguardUserDetailItemView newView = (KeyguardUserDetailItemView)
+ mAdapter.getView(i, oldView, mListView);
+ UserSwitcherController.UserRecord userTag =
+ (UserSwitcherController.UserRecord) newView.getTag();
+ if (userTag.isCurrent) {
+ if (i != 0) {
+ Log.w(TAG, "Current user is not the first view in the list");
+ }
+ foundCurrentUser = true;
+ mCurrentUserId = userTag.info.id;
+ mCurrentUserIsGuest = userTag.isGuest;
+ // Current user is always visible
+ newView.updateVisibilities(true /* showItem */,
+ mUserSwitcherOpen /* showTextName */, false /* animate */);
+ } else {
+ // Views for non-current users are always expanded (e.g. they should the name
+ // next to the user icon). However, they could be hidden entirely if the list
+ // is closed.
+ newView.updateVisibilities(mUserSwitcherOpen /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+ newView.setDarkAmount(mDarkAmount);
+ if (oldView == null) {
+ // We ran out of existing views. Add it at the end.
+ mListView.addView(newView);
+ } else if (oldView != newView) {
+ // We couldn't rebind the view. Replace it.
+ mListView.replaceView(newView, i);
+ }
+ } else {
+ mListView.removeLastView();
+ }
+ }
+ if (!foundCurrentUser) {
+ Log.w(TAG, "Current user is not listed");
+ mCurrentUserId = UserHandle.USER_NULL;
+ mCurrentUserIsGuest = false;
+ }
+ }
+
+ /**
+ * Get the height of the keyguard user switcher view when closed.
+ */
+ public int getUserIconHeight() {
+ View firstChild = mListView.getChildAt(0);
+ return firstChild == null ? 0 : firstChild.getHeight();
+ }
+
+ /**
+ * Set the visibility of the keyguard user switcher view based on some new state.
+ */
+ public void setKeyguardUserSwitcherVisibility(
+ int statusBarState,
+ boolean keyguardFadingAway,
+ boolean goingToFullShade,
+ int oldStatusBarState) {
+ mKeyguardVisibilityHelper.setViewVisibility(
+ statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
+ }
+
+ /**
+ * Update position of the view with an optional animation
+ */
+ public void updatePosition(int x, int y, boolean animate) {
+ PropertyAnimator.setProperty(mListView, AnimatableProperty.Y, y, ANIMATION_PROPERTIES,
+ animate);
+ PropertyAnimator.setProperty(mListView, AnimatableProperty.TRANSLATION_X, -Math.abs(x),
+ ANIMATION_PROPERTIES, animate);
+ }
+
+ /**
+ * Set keyguard user switcher view alpha.
+ */
+ public void setAlpha(float alpha) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+ mView.setAlpha(alpha);
+ }
+ }
+
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ private void setDarkAmount(float darkAmount) {
+ boolean isAwake = darkAmount != 0;
+ if (darkAmount == mDarkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ mListView.setDarkAmount(darkAmount);
+ mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
+ if (!isAwake) {
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ }
+
+ private boolean isListAnimating() {
+ return mKeyguardVisibilityHelper.isVisibilityAnimating() || mListView.isAnimating();
+ }
+
+ /**
+ * Remove the callback if it exists.
+ */
+ public void removeCallback() {
+ if (DEBUG) Log.d(TAG, "removeCallback");
+ mKeyguardUserSwitcherCallback = null;
+ }
+
+ /**
+ * Register to receive notifications about keyguard user switcher state
+ * (see {@link KeyguardUserSwitcherListener}.
+ *
+ * Only one callback can be used at a time.
+ *
+ * @param callback The callback to register
+ */
+ public void setCallback(KeyguardUserSwitcherListener callback) {
+ if (DEBUG) Log.d(TAG, "setCallback");
+ mKeyguardUserSwitcherCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * If user switcher state changes, notifies all {@link KeyguardUserSwitcherListener}.
+ * Switcher state is updatd before animations finish.
+ *
+ * @param animate true to animate transition. The user switcher state (i.e.
+ * {@link #isUserSwitcherOpen()}) is updated before animation is finished.
+ */
+ private void setUserSwitcherOpened(boolean open, boolean animate) {
+ boolean wasOpen = mUserSwitcherOpen;
+ if (DEBUG) {
+ Log.d(TAG, String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", wasOpen,
+ open, animate));
+ }
+ mUserSwitcherOpen = open;
+ if (mUserSwitcherOpen != wasOpen) {
+ notifyUserSwitcherStateChanged();
+ }
+ updateVisibilities(animate);
+ }
+
+ private void updateVisibilities(boolean animate) {
+ if (DEBUG) Log.d(TAG, String.format("updateVisibilities: animate=%b", animate));
+ mEndGuestButton.animate().cancel();
+ if (mUserSwitcherOpen && mCurrentUserIsGuest) {
+ // Show the "End guest session" button
+ mEndGuestButton.setVisibility(View.VISIBLE);
+ if (animate) {
+ mEndGuestButton.setAlpha(0f);
+ mEndGuestButton.animate()
+ .alpha(1f)
+ .setDuration(360)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .withEndAction(() -> {
+ mEndGuestButton.setClickable(true);
+ });
+ } else {
+ mEndGuestButton.setClickable(true);
+ mEndGuestButton.setAlpha(1f);
+ }
+ } else {
+ // Hide the "End guest session" button. If it's already GONE, don't try to
+ // animate it or it will appear again for an instant.
+ mEndGuestButton.setClickable(false);
+ if (animate && mEndGuestButton.getVisibility() != View.GONE) {
+ mEndGuestButton.setVisibility(View.VISIBLE);
+ mEndGuestButton.setAlpha(1f);
+ mEndGuestButton.animate()
+ .alpha(0f)
+ .setDuration(360)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(() -> {
+ mEndGuestButton.setVisibility(View.GONE);
+ mEndGuestButton.setAlpha(1f);
+ });
+ } else {
+ mEndGuestButton.setVisibility(View.GONE);
+ mEndGuestButton.setAlpha(1f);
+ }
+ }
+
+ mListView.updateVisibilities(mUserSwitcherOpen, animate);
+ }
+
+ private boolean isUserSwitcherOpen() {
+ return mUserSwitcherOpen;
+ }
+
+ private void notifyUserSwitcherStateChanged() {
+ if (DEBUG) {
+ Log.d(TAG, String.format("notifyUserSwitcherStateChanged: mUserSwitcherOpen=%b",
+ mUserSwitcherOpen));
+ }
+ if (mKeyguardUserSwitcherCallback != null) {
+ KeyguardUserSwitcherListener cb = mKeyguardUserSwitcherCallback.get();
+ if (cb != null) {
+ cb.onKeyguardUserSwitcherChanged(mUserSwitcherOpen);
+ }
+ }
+ }
+
+ /**
+ * Callback for keyguard user switcher state information
+ */
+ public interface KeyguardUserSwitcherListener {
+
+ /**
+ * Called when the keyguard enters or leaves user switcher mode. This will be called
+ * before the animations are finished.
+ *
+ * @param open if true, keyguard is showing the user switcher or transitioning from/to user
+ * switcher mode.
+ */
+ void onKeyguardUserSwitcherChanged(boolean open);
+ }
+
+ static class KeyguardUserAdapter extends
+ UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
+
+ private final Context mContext;
+ private final Resources mResources;
+ private final LayoutInflater mLayoutInflater;
+ private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
+ private View mCurrentUserView;
+ // List of users where the first entry is always the current user
+ private ArrayList<UserSwitcherController.UserRecord> mUsersOrdered = new ArrayList<>();
+
+ KeyguardUserAdapter(Context context, Resources resources, LayoutInflater layoutInflater,
+ UserSwitcherController controller,
+ KeyguardUserSwitcherController keyguardUserSwitcherController) {
+ super(controller);
+ mContext = context;
+ mResources = resources;
+ mLayoutInflater = layoutInflater;
+ mKeyguardUserSwitcherController = keyguardUserSwitcherController;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ // At this point, value of isSimpleUserSwitcher() may have changed in addition to the
+ // data set
+ refreshUserOrder();
+ super.notifyDataSetChanged();
+ }
+
+ void refreshUserOrder() {
+ ArrayList<UserSwitcherController.UserRecord> users = super.getUsers();
+ mUsersOrdered = new ArrayList<>(users.size());
+ for (int i = 0; i < users.size(); i++) {
+ UserSwitcherController.UserRecord record = users.get(i);
+ if (record.isCurrent) {
+ mUsersOrdered.add(0, record);
+ } else {
+ mUsersOrdered.add(record);
+ }
+ }
+ }
+
+ @Override
+ protected ArrayList<UserSwitcherController.UserRecord> getUsers() {
+ return mUsersOrdered;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ UserSwitcherController.UserRecord item = getItem(position);
+ return createUserDetailItemView(convertView, parent, item);
+ }
+
+ @Override
+ public String getName(Context context, UserSwitcherController.UserRecord item) {
+ if (item.isGuest) {
+ return context.getString(com.android.settingslib.R.string.guest_nickname);
+ } else {
+ return super.getName(context, item);
+ }
+ }
+
+ KeyguardUserDetailItemView convertOrInflate(View convertView, ViewGroup parent) {
+ if (!(convertView instanceof KeyguardUserDetailItemView)
+ || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
+ convertView = mLayoutInflater.inflate(
+ R.layout.keyguard_user_switcher_item, parent, false);
+ }
+ return (KeyguardUserDetailItemView) convertView;
+ }
+
+ KeyguardUserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
+ UserSwitcherController.UserRecord item) {
+ KeyguardUserDetailItemView v = convertOrInflate(convertView, parent);
+ v.setOnClickListener(this);
+
+ String name = getName(mContext, item);
+ if (item.picture == null) {
+ v.bind(name, getDrawable(item).mutate(), item.resolveId());
+ } else {
+ int avatarSize =
+ (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
+ drawable.setColorFilter(
+ item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
+ v.bind(name, drawable, item.info.id);
+ }
+ v.setActivated(item.isCurrent);
+ v.setDisabledByAdmin(item.isDisabledByAdmin);
+ v.setEnabled(item.isSwitchToEnabled);
+ v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
+
+ if (item.isCurrent) {
+ mCurrentUserView = v;
+ }
+ v.setTag(item);
+ return v;
+ }
+
+ private Drawable getDrawable(UserSwitcherController.UserRecord item) {
+ Drawable drawable;
+ if (item.isCurrent && item.isGuest) {
+ drawable = mContext.getDrawable(R.drawable.ic_avatar_guest_user);
+ } else {
+ drawable = getIconDrawable(mContext, item);
+ }
+
+ int iconColorRes;
+ if (item.isSwitchToEnabled) {
+ iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
+ } else {
+ iconColorRes = R.color.kg_user_switcher_restricted_avatar_icon_color;
+ }
+ drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme()));
+
+ Drawable bg = mContext.getDrawable(R.drawable.kg_bg_avatar);
+ drawable = new LayerDrawable(new Drawable[]{bg, drawable});
+ return drawable;
+ }
+
+ @Override
+ public void onClick(View v) {
+ UserSwitcherController.UserRecord user = (UserSwitcherController.UserRecord) v.getTag();
+
+ if (mKeyguardUserSwitcherController.isListAnimating()) {
+ return;
+ }
+
+ if (mKeyguardUserSwitcherController.isUserSwitcherOpen()) {
+ if (user.isCurrent) {
+ // Close the switcher if tapping the current user
+ mKeyguardUserSwitcherController.setUserSwitcherOpened(
+ false /* open */, true /* animate */);
+ } else if (user.isSwitchToEnabled) {
+ if (!user.isAddUser && !user.isRestricted && !user.isDisabledByAdmin) {
+ if (mCurrentUserView != null) {
+ mCurrentUserView.setActivated(false);
+ }
+ v.setActivated(true);
+ }
+ onUserListItemClicked(user);
+ }
+ } else {
+ // If switcher is closed, tapping anywhere in the view will open it
+ mKeyguardUserSwitcherController.setUserSwitcherOpened(
+ true /* open */, true /* animate */);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
new file mode 100644
index 000000000000..7c82c116eb3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.keyguard.KeyguardConstants;
+import com.android.settingslib.animation.AppearAnimationUtils;
+import com.android.settingslib.animation.DisappearAnimationUtils;
+import com.android.systemui.Interpolators;
+
+/**
+ * The container for the user switcher on Keyguard.
+ */
+public class KeyguardUserSwitcherListView extends AlphaOptimizedLinearLayout {
+
+ private static final String TAG = "KeyguardUserSwitcherListView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private static final int ANIMATION_DURATION_OPENING = 360;
+ private static final int ANIMATION_DURATION_CLOSING = 240;
+
+ private boolean mAnimating;
+ private final AppearAnimationUtils mAppearAnimationUtils;
+ private final DisappearAnimationUtils mDisappearAnimationUtils;
+
+ public KeyguardUserSwitcherListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setClipChildren(false);
+ mAppearAnimationUtils = new AppearAnimationUtils(context, ANIMATION_DURATION_OPENING,
+ -0.5f, 0.5f, Interpolators.FAST_OUT_SLOW_IN);
+ mDisappearAnimationUtils = new DisappearAnimationUtils(context, ANIMATION_DURATION_CLOSING,
+ 0.5f, 0.5f, Interpolators.FAST_OUT_LINEAR_IN);
+ }
+
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ void setDarkAmount(float darkAmount) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ if (v instanceof KeyguardUserDetailItemView) {
+ ((KeyguardUserDetailItemView) v).setDarkAmount(darkAmount);
+ }
+ }
+ }
+
+ boolean isAnimating() {
+ return mAnimating;
+ }
+
+ /**
+ * Update visibilities of this view and child views for when the user list is open or closed.
+ * If closed, this hides everything but the first item (which is always the current user).
+ */
+ void updateVisibilities(boolean open, boolean animate) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("updateVisibilities: open=%b animate=%b childCount=%d",
+ open, animate, getChildCount()));
+ }
+
+ mAnimating = false;
+
+ int userListCount = getChildCount();
+ if (userListCount > 0) {
+ // The first child is always the current user.
+ KeyguardUserDetailItemView currentUserView = ((KeyguardUserDetailItemView) getChildAt(
+ 0));
+ currentUserView.updateVisibilities(true /* showItem */, open /* showTextName */,
+ animate);
+ currentUserView.setClickable(true);
+ currentUserView.clearAnimation();
+ }
+
+ if (userListCount <= 1) {
+ return;
+ }
+
+ if (animate) {
+ // Create an array of all the remaining users (that aren't the current user).
+ KeyguardUserDetailItemView[] otherUserViews =
+ new KeyguardUserDetailItemView[userListCount - 1];
+ for (int i = 1, n = 0; i < userListCount; i++, n++) {
+ otherUserViews[n] = (KeyguardUserDetailItemView) getChildAt(i);
+
+ // Update clickable state immediately so that the menu feels more responsive
+ otherUserViews[n].setClickable(open);
+
+ // Before running the animation, ensure visibility is set correctly
+ otherUserViews[n].updateVisibilities(
+ true /* showItem */, true /* showTextName */, false /* animate */);
+ otherUserViews[n].clearAnimation();
+ }
+
+ setClipChildren(false);
+ setClipToPadding(false);
+
+ mAnimating = true;
+
+ final int nonCurrentUserCount = otherUserViews.length;
+ if (open) {
+ mAppearAnimationUtils.startAnimation(otherUserViews, () -> {
+ setClipChildren(true);
+ setClipToPadding(true);
+ mAnimating = false;
+ });
+ } else {
+ mDisappearAnimationUtils.startAnimation(otherUserViews, () -> {
+ setClipChildren(true);
+ setClipToPadding(true);
+ for (int i = 0; i < nonCurrentUserCount; i++) {
+ otherUserViews[i].updateVisibilities(
+ false /* showItem */, true /* showTextName */, false /* animate */);
+ }
+ mAnimating = false;
+ });
+ }
+ } else {
+ for (int i = 1; i < userListCount; i++) {
+ KeyguardUserDetailItemView nonCurrentUserView =
+ ((KeyguardUserDetailItemView) getChildAt(i));
+ nonCurrentUserView.clearAnimation();
+ nonCurrentUserView.updateVisibilities(
+ open /* showItem */, true /* showTextName */, false /* animate */);
+ nonCurrentUserView.setClickable(open);
+ }
+ }
+ }
+
+ /**
+ * Replaces the view at the specified position in the group.
+ *
+ * @param index the position in the group of the view to remove
+ */
+ void replaceView(KeyguardUserDetailItemView newView, int index) {
+ removeViewAt(index);
+ addView(newView, index);
+ }
+
+ /**
+ * Removes the last view in the group.
+ */
+ void removeLastView() {
+ int lastIndex = getChildCount() - 1;
+ removeViewAt(lastIndex);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java
new file mode 100644
index 000000000000..3f0e23f7c72e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * The container for the user switcher on Keyguard.
+ */
+public class KeyguardUserSwitcherView extends FrameLayout {
+
+ public KeyguardUserSwitcherView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 68d74ef760b4..1838391ce2ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -423,7 +423,7 @@ public class UserSwitcherController implements Dumpable {
}
}
- private void showExitGuestDialog(int id) {
+ protected void showExitGuestDialog(int id) {
int newId = UserHandle.USER_SYSTEM;
if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 72f1f22c0ba1..fd3641cfdaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -20,12 +20,15 @@ import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.View;
+import com.android.systemui.R;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import java.util.HashSet;
import java.util.List;
@@ -163,4 +166,15 @@ public class Utils {
}
return apps;
}
+
+ /**
+ * Returns true if the device should use the split notification shade, based on feature flags,
+ * orientation and screen width.
+ */
+ public static boolean shouldUseSplitNotificationShade(FeatureFlags featureFlags,
+ Resources resources) {
+ return featureFlags.isTwoColumnNotificationShadeEnabled()
+ && resources.getBoolean(R.bool.config_use_split_notification_shade);
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 0795d89eb0bc..ff2881953342 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -19,6 +19,7 @@ package com.android.systemui.wmshell;
import android.content.Context;
import android.os.Handler;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -28,6 +29,7 @@ import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
@@ -49,7 +51,7 @@ import dagger.Module;
import dagger.Provides;
/**
- * Dagger module for TV Pip.
+ * Provides TV specific dependencies for Pip.
*/
@Module(includes = {WMShellBaseModule.class})
public abstract class TvPipModule {
@@ -143,7 +145,8 @@ public abstract class TvPipModule {
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
+ Optional<LegacySplitScreenController> splitScreenOptional,
+ DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index f23367b4d65b..141b9f7d410d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -20,6 +20,7 @@ import android.animation.AnimationHandler;
import android.content.Context;
import android.view.IWindowManager;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -39,11 +40,20 @@ import dagger.Module;
import dagger.Provides;
/**
- * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
- * branches of SystemUI.
+ * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
+ * accessible from components within the WM subcomponent (can be explicitly exposed to the
+ * SysUIComponent, see {@link WMComponent}).
+ *
+ * This module only defines Shell dependencies for the TV SystemUI implementation. Common
+ * dependencies should go into {@link WMShellBaseModule}.
*/
@Module(includes = {TvPipModule.class})
public class TvWMShellModule {
+
+ //
+ // Internal common - Components used internally by multiple shell features
+ //
+
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
@@ -53,16 +63,20 @@ public class TvWMShellModule {
transactionPool);
}
+ //
+ // Split/multiwindow
+ //
+
@WMSingleton
@Provides
- static LegacySplitScreen provideSplitScreen(Context context,
+ static LegacySplitScreenController provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, TransactionPool transactionPool,
ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
TaskStackListenerImpl taskStackListener, Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor,
@ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
- return LegacySplitScreenController.create(context, displayController, systemWindows,
+ return new LegacySplitScreenController(context, displayController, systemWindows,
displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 81ac21c00823..ec61db591324 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
@@ -73,7 +74,20 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
/**
- * Proxy in SysUiScope to delegate events to controllers in WM Shell library.
+ * A SystemUI service that starts with the SystemUI application and sets up any bindings between
+ * Shell and SysUI components. This service starts happens after the {@link WMComponent} has
+ * already been initialized and may only reference Shell components that are explicitly exported to
+ * SystemUI (see {@link WMComponent}.
+ *
+ * eg. SysUI application starts
+ * -> SystemUIFactory is initialized
+ * -> WMComponent is created
+ * -> WMShellBaseModule dependencies are injected
+ * -> WMShellModule (form-factory specific) dependencies are injected
+ * -> SysUIComponent is created
+ * -> WMComponents are explicitly provided to SysUIComponent for injection into SysUI code
+ * -> SysUI services are started
+ * -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
*/
@SysUISingleton
public final class WMShell extends SystemUI
@@ -142,6 +156,8 @@ public final class WMShell extends SystemUI
@Override
public void start() {
+ // TODO: Consider piping config change and other common calls to a shell component to
+ // delegate internally
mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index b42dde63d1c2..449db61a0fbb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -32,6 +32,7 @@ import android.view.WindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.FullscreenTaskListener;
@@ -45,6 +46,7 @@ import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.TaskViewFactoryController;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
@@ -63,6 +65,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -70,8 +73,8 @@ import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
+import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -85,8 +88,13 @@ import dagger.Module;
import dagger.Provides;
/**
- * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here
- * should be shared among different branches of SystemUI.
+ * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
+ * accessible from components within the WM subcomponent (can be explicitly exposed to the
+ * SysUIComponent, see {@link WMComponent}).
+ *
+ * This module only defines *common* dependencies across various SystemUI implementations,
+ * dependencies that are device/form factor SystemUI implementation specific should go into their
+ * respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.)
*/
@Module
public abstract class WMShellBaseModule {
@@ -174,72 +182,65 @@ public abstract class WMShellBaseModule {
}
}
+ //
+ // Internal common - Components used internally by multiple shell features
+ //
+
@WMSingleton
@Provides
- static ShellInit provideShellInit(DisplayImeController displayImeController,
- DragAndDropController dragAndDropController,
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairs> appPairsOptional,
- FullscreenTaskListener fullscreenTaskListener,
- Transitions transitions,
- @ShellMainThread ShellExecutor mainExecutor) {
- return ShellInitImpl.create(displayImeController,
- dragAndDropController,
- shellTaskOrganizer,
- legacySplitScreenOptional,
- splitScreenOptional,
- appPairsOptional,
- fullscreenTaskListener,
- transitions,
- mainExecutor);
+ static DisplayController provideDisplayController(Context context,
+ IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
+ return new DisplayController(context, wmService, mainExecutor);
}
- /**
- * Note, this is only optional because we currently pass this to the SysUI component scope and
- * for non-primary users, we may inject a null-optional for that dependency.
- */
@WMSingleton
@Provides
- static Optional<ShellCommandHandler> provideShellCommandHandler(
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional,
+ static DragAndDropController provideDragAndDropController(Context context,
+ DisplayController displayController) {
+ return new DragAndDropController(context, displayController);
+ }
+
+ @WMSingleton
+ @Provides
+ static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
+ Context context, SizeCompatUIController sizeCompatUI) {
+ return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
+ }
+
+ @WMSingleton
+ @Provides
+ static SizeCompatUIController provideSizeCompatUIController(Context context,
+ DisplayController displayController, DisplayImeController imeController,
@ShellMainThread ShellExecutor mainExecutor) {
- return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
- legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
- hideDisplayCutout, appPairsOptional, mainExecutor));
+ return new SizeCompatUIController(context, displayController, imeController, mainExecutor);
}
@WMSingleton
@Provides
- static TransactionPool provideTransactionPool() {
- return new TransactionPool();
+ static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new SyncTransactionQueue(pool, mainExecutor);
}
@WMSingleton
@Provides
- static DisplayController provideDisplayController(Context context,
- IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
- return new DisplayController(context, wmService, mainExecutor);
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
}
+ // We currently dedupe multiple messages, so we use the shell main handler directly
@WMSingleton
@Provides
- static DragAndDropController provideDragAndDropController(Context context,
- DisplayController displayController) {
- return new DragAndDropController(context, displayController);
+ static TaskStackListenerImpl providerTaskStackListenerImpl(
+ @ShellMainThread Handler mainHandler) {
+ return new TaskStackListenerImpl(mainHandler);
}
@WMSingleton
@Provides
- static FloatingContentCoordinator provideFloatingContentCoordinator() {
- return new FloatingContentCoordinator();
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
}
@WMSingleton
@@ -249,10 +250,99 @@ public abstract class WMShellBaseModule {
return new WindowManagerShellWrapper(mainExecutor);
}
+ //
+ // Bubbles
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<Bubbles> provideBubbles(Optional<BubbleController> bubbleController) {
+ return bubbleController.map((controller) -> controller.asBubbles());
+ }
+
+ // Note: Handler needed for LauncherApps.register
+ @WMSingleton
+ @Provides
+ static Optional<BubbleController> provideBubbleController(Context context,
+ FloatingContentCoordinator floatingContentCoordinator,
+ IStatusBarService statusBarService,
+ WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ LauncherApps launcherApps,
+ UiEventLogger uiEventLogger,
+ ShellTaskOrganizer organizer,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return Optional.of(BubbleController.create(context, null /* synchronizer */,
+ floatingContentCoordinator, statusBarService, windowManager,
+ windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
+ mainExecutor, mainHandler));
+ }
+
+ //
+ // Fullscreen
+ //
+
+ @WMSingleton
+ @Provides
+ static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) {
+ return new FullscreenTaskListener(syncQueue);
+ }
+
+ //
+ // Hide display cutout
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<HideDisplayCutout> provideHideDisplayCutout(
+ Optional<HideDisplayCutoutController> hideDisplayCutoutController) {
+ return hideDisplayCutoutController.map((controller) -> controller.asHideDisplayCutout());
+ }
+
+ @WMSingleton
+ @Provides
+ static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context,
+ DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) {
+ return Optional.ofNullable(
+ HideDisplayCutoutController.create(context, displayController, mainExecutor));
+ }
+
+ //
+ // One handed mode (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<OneHanded> provideOneHanded(Optional<OneHandedController> oneHandedController) {
+ return oneHandedController.map((controller) -> controller.asOneHanded());
+ }
+
+ // Needs the shell main handler for ContentObserver callbacks
+ @WMSingleton
+ @Provides
+ static Optional<OneHandedController> provideOneHandedController(Context context,
+ DisplayController displayController, TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return Optional.ofNullable(OneHandedController.create(context, displayController,
+ taskStackListener, uiEventLogger, mainExecutor, mainHandler));
+ }
+
+ //
+ // Pip (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
+ static FloatingContentCoordinator provideFloatingContentCoordinator() {
+ return new FloatingContentCoordinator();
+ }
+
@WMSingleton
@Provides
static PipAppOpsListener providePipAppOpsListener(Context context,
- IActivityManager activityManager,
PipTouchHandler pipTouchHandler,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
@@ -268,38 +358,39 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
- PackageManager packageManager) {
- return new PipUiEventLogger(uiEventLogger, packageManager);
- }
-
- @WMSingleton
- @Provides
static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
return new PipSurfaceTransactionHelper(context);
}
@WMSingleton
@Provides
- static SystemWindows provideSystemWindows(DisplayController displayController,
- IWindowManager wmService) {
- return new SystemWindows(displayController, wmService);
+ static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
+ PackageManager packageManager) {
+ return new PipUiEventLogger(uiEventLogger, packageManager);
}
+ //
+ // Shell transitions
+ //
+
@WMSingleton
@Provides
- static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new SyncTransactionQueue(pool, mainExecutor);
+ static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
+ return Transitions.asRemoteTransitions(transitions);
}
@WMSingleton
@Provides
- static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
- Context context, SizeCompatUI sizeCompatUI) {
- return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
+ static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellAnimationThread ShellExecutor animExecutor) {
+ return new Transitions(organizer, pool, mainExecutor, animExecutor);
}
+ //
+ // Split/multiwindow
+ //
+
@WMSingleton
@Provides
static RootTaskDisplayAreaOrganizer provideRootTaskDisplayAreaOrganizer(
@@ -307,17 +398,6 @@ public abstract class WMShellBaseModule {
return new RootTaskDisplayAreaOrganizer(mainExecutor, context);
}
- // We currently dedupe multiple messages, so we use the shell main handler directly
- @WMSingleton
- @Provides
- static TaskStackListenerImpl providerTaskStackListenerImpl(
- @ShellMainThread Handler mainHandler) {
- return new TaskStackListenerImpl(mainHandler);
- }
-
- @BindsOptionalOf
- abstract LegacySplitScreen optionalLegacySplitScreen();
-
@WMSingleton
@Provides
static Optional<SplitScreen> provideSplitScreen(
@@ -340,81 +420,91 @@ public abstract class WMShellBaseModule {
}
}
- @BindsOptionalOf
- abstract AppPairs optionalAppPairs();
+ // Legacy split (optional feature)
- // Note: Handler needed for LauncherApps.register
@WMSingleton
@Provides
- static Optional<Bubbles> provideBubbles(Context context,
- FloatingContentCoordinator floatingContentCoordinator,
- IStatusBarService statusBarService,
- WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps,
- UiEventLogger uiEventLogger,
- ShellTaskOrganizer organizer,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
- return Optional.of(BubbleController.create(context, null /* synchronizer */,
- floatingContentCoordinator, statusBarService, windowManager,
- windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
- mainExecutor, mainHandler));
+ static Optional<LegacySplitScreen> provideLegacySplitScreen(
+ Optional<LegacySplitScreenController> splitScreenController) {
+ return splitScreenController.map((controller) -> controller.asLegacySplitScreen());
}
- // Needs the shell main handler for ContentObserver callbacks
- @WMSingleton
- @Provides
- static Optional<OneHanded> provideOneHandedController(Context context,
- DisplayController displayController, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
- return Optional.ofNullable(OneHandedController.create(context, displayController,
- taskStackListener, uiEventLogger, mainExecutor, mainHandler));
- }
+ @BindsOptionalOf
+ abstract LegacySplitScreenController optionalLegacySplitScreenController();
- @WMSingleton
- @Provides
- static Optional<HideDisplayCutout> provideHideDisplayCutoutController(Context context,
- DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(
- HideDisplayCutoutController.create(context, displayController, mainExecutor));
- }
+ // App Pairs (optional feature)
@WMSingleton
@Provides
- static Optional<TaskViewFactory> provideTaskViewFactory(ShellTaskOrganizer shellTaskOrganizer,
- @ShellMainThread ShellExecutor mainExecutor) {
- return Optional.of(new TaskViewFactoryController(shellTaskOrganizer, mainExecutor)
- .getTaskViewFactory());
+ static Optional<AppPairs> provideAppPairs(Optional<AppPairsController> appPairsController) {
+ return appPairsController.map((controller) -> controller.asAppPairs());
}
+ @BindsOptionalOf
+ abstract AppPairsController optionalAppPairs();
+
+ //
+ // Task view factory
+ //
+
@WMSingleton
@Provides
- static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) {
- return new FullscreenTaskListener(syncQueue);
+ static Optional<TaskViewFactory> provideTaskViewFactory(
+ TaskViewFactoryController taskViewFactoryController) {
+ return Optional.of(taskViewFactoryController.asTaskViewFactory());
}
@WMSingleton
@Provides
- static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
- return Transitions.asRemoteTransitions(transitions);
+ static TaskViewFactoryController provideTaskViewFactoryController(
+ ShellTaskOrganizer shellTaskOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor);
}
+ //
+ // Misc
+ //
+
@WMSingleton
@Provides
- static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellAnimationThread ShellExecutor animExecutor) {
- return new Transitions(organizer, pool, mainExecutor, animExecutor);
+ static ShellInit provideShellInit(DisplayImeController displayImeController,
+ DragAndDropController dragAndDropController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
+ Optional<AppPairsController> appPairsOptional,
+ FullscreenTaskListener fullscreenTaskListener,
+ Transitions transitions,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return ShellInitImpl.create(displayImeController,
+ dragAndDropController,
+ shellTaskOrganizer,
+ legacySplitScreenOptional,
+ splitScreenOptional,
+ appPairsOptional,
+ fullscreenTaskListener,
+ transitions,
+ mainExecutor);
}
+ /**
+ * Note, this is only optional because we currently pass this to the SysUI component scope and
+ * for non-primary users, we may inject a null-optional for that dependency.
+ */
@WMSingleton
@Provides
- static SizeCompatUI provideSizeCompatUI(Context context, DisplayController displayController,
- DisplayImeController imeController, @ShellMainThread ShellExecutor mainExecutor) {
- return SizeCompatUIController.create(context, displayController, imeController,
- mainExecutor);
+ static Optional<ShellCommandHandler> provideShellCommandHandler(
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
+ Optional<Pip> pipOptional,
+ Optional<OneHandedController> oneHandedOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutout,
+ Optional<AppPairsController> appPairsOptional,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
+ legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
+ hideDisplayCutout, appPairsOptional, mainExecutor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 2aaa0951d9d9..997b488a627f 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -21,10 +21,10 @@ import android.content.Context;
import android.os.Handler;
import android.view.IWindowManager;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
@@ -36,7 +36,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -60,11 +59,20 @@ import dagger.Module;
import dagger.Provides;
/**
- * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
- * branches of SystemUI.
+ * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
+ * accessible from components within the WM subcomponent (can be explicitly exposed to the
+ * SysUIComponent, see {@link WMComponent}).
+ *
+ * This module only defines Shell dependencies for handheld SystemUI implementation. Common
+ * dependencies should go into {@link WMShellBaseModule}.
*/
@Module(includes = WMShellBaseModule.class)
public class WMShellModule {
+
+ //
+ // Internal common - Components used internally by multiple shell features
+ //
+
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
@@ -74,29 +82,37 @@ public class WMShellModule {
transactionPool);
}
+ //
+ // Split/multiwindow
+ //
+
@WMSingleton
@Provides
- static LegacySplitScreen provideLegacySplitScreen(Context context,
+ static LegacySplitScreenController provideLegacySplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, TransactionPool transactionPool,
ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
TaskStackListenerImpl taskStackListener, Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor,
@ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
- return LegacySplitScreenController.create(context, displayController, systemWindows,
+ return new LegacySplitScreenController(context, displayController, systemWindows,
displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
}
@WMSingleton
@Provides
- static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
+ static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor) {
- return AppPairsController.create(shellTaskOrganizer, syncQueue, displayController,
+ return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
mainExecutor);
}
+ //
+ // Pip
+ //
+
@WMSingleton
@Provides
static Optional<Pip> providePip(Context context, DisplayController displayController,
@@ -161,7 +177,8 @@ public class WMShellModule {
PipAnimationController pipAnimationController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
- Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
+ Optional<LegacySplitScreenController> splitScreenOptional,
+ DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
new file mode 100644
index 000000000000..9278570714fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.globalactions.GlobalActionsComponent
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.wm.shell.TaskViewFactory
+import dagger.Lazy
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ControlActionCoordinatorImplTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var uiController: ControlsUiController
+ @Mock
+ private lateinit var lazyUiController: Lazy<ControlsUiController>
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var bgExecutor: DelayableExecutor
+ @Mock
+ private lateinit var uiExecutor: DelayableExecutor
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var globalActionsComponent: GlobalActionsComponent
+ @Mock
+ private lateinit var taskViewFactory: Optional<TaskViewFactory>
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var cvh: ControlViewHolder
+
+ companion object {
+ fun <T> any(): T = Mockito.any<T>()
+
+ private val ID = "id"
+ }
+
+ private lateinit var coordinator: ControlActionCoordinatorImpl
+ private lateinit var action: ControlActionCoordinatorImpl.Action
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ coordinator = spy(ControlActionCoordinatorImpl(
+ mContext,
+ bgExecutor,
+ uiExecutor,
+ activityStarter,
+ keyguardStateController,
+ globalActionsComponent,
+ taskViewFactory,
+ getFakeBroadcastDispatcher(),
+ lazyUiController
+ ))
+
+ `when`(cvh.cws.ci.controlId).thenReturn(ID)
+ action = spy(coordinator.Action(ID, {}, false))
+ doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean())
+ }
+
+ @Test
+ fun testToggleRunsWhenUnlocked() {
+ `when`(keyguardStateController.isShowing()).thenReturn(false)
+
+ coordinator.toggle(cvh, "", true)
+ verify(coordinator).bouncerOrRun(action)
+ verify(action).invoke()
+ }
+
+ @Test
+ fun testToggleDoesNotRunWhenLockedThenRunsWhenUnlocked() {
+ `when`(keyguardStateController.isShowing()).thenReturn(true)
+ `when`(keyguardStateController.isUnlocked()).thenReturn(false)
+
+ coordinator.toggle(cvh, "", true)
+ verify(coordinator).bouncerOrRun(action)
+ verify(activityStarter).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+ verify(action, never()).invoke()
+
+ // Simulate a refresh call from a Publisher, which will trigger a call to runPendingAction
+ reset(action)
+ coordinator.runPendingAction(ID)
+ verify(action, never()).invoke()
+
+ `when`(keyguardStateController.isUnlocked()).thenReturn(true)
+ reset(action)
+ coordinator.runPendingAction(ID)
+ verify(action).invoke()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 0dc268a3c186..fb817eac3e10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -28,6 +30,8 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -37,6 +41,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
@@ -44,6 +49,7 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.animation.DisappearParameters;
import org.junit.Before;
@@ -86,17 +92,25 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
QSTileView mQSTileView;
@Mock
PagedTileLayout mPagedTileLayout;
+ @Mock
+ FeatureFlags mFeatureFlags;
+ @Mock
+ Resources mResources;
+ @Mock
+ Configuration mConfiguration;
private QSPanelControllerBase<QSPanel> mController;
+
+
/** Implementation needed to ensure we have a reflectively-available class name. */
private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager) {
+ DumpManager dumpManager, FeatureFlags featureFlags) {
super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
- qsLogger, dumpManager);
+ qsLogger, dumpManager, featureFlags);
}
@Override
@@ -121,10 +135,12 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
when(mQSTileRevealControllerFactory.create(any(), any()))
.thenReturn(mQSTileRevealController);
when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
+ when(mQSPanel.getResources()).thenReturn(mResources);
+ when(mResources.getConfiguration()).thenReturn(mConfiguration);
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
mController.init();
reset(mQSTileRevealController);
@@ -136,7 +152,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
mQSTileHost, mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags) {
@Override
protected QSTileRevealController createTileRevealController() {
return mQSTileRevealController;
@@ -218,4 +234,18 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
verify(mQSLogger).logAllTilesChangeListening(false, "QSPanel", "dnd");
verify(mPagedTileLayout).setListening(false, mUiEventLogger);
}
+
+
+ @Test
+ public void testShouldUzeHorizontalLayout_falseForSplitShade() {
+ mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ when(mMediaHost.getVisible()).thenReturn(true);
+
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ assertThat(mController.shouldUseHorizontalLayout()).isTrue();
+
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ assertThat(mController.shouldUseHorizontalLayout()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 4381158c3415..0dfebab59feb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -41,6 +41,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.settings.brightness.ToggleSlider;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.animation.DisappearParameters;
@@ -93,6 +94,8 @@ public class QSPanelControllerTest extends SysuiTestCase {
QSTileView mQSTileView;
@Mock
PagedTileLayout mPagedTileLayout;
+ @Mock
+ FeatureFlags mFeatureFlags;
private QSPanelController mController;
@@ -118,7 +121,9 @@ public class QSPanelControllerTest extends SysuiTestCase {
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- /* labelsFlag */ false);
+ /* labelsFlag */ false,
+ mFeatureFlags
+ );
mController.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 107160f47f02..587020090433 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.statusbar.FeatureFlags
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -63,6 +64,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
private lateinit var tileLayout: TileLayout
@Mock
private lateinit var tileView: QSTileView
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
private lateinit var controller: QuickQSPanelController
@@ -84,7 +87,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
uiEventLogger,
qsLogger,
dumpManager,
- false
+ false,
+ featureFlags
)
controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
new file mode 100644
index 000000000000..b7b967866d47
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.os.Handler
+import android.provider.Settings
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.lifecycle.LifecycleOwner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.dagger.ControlsComponent
+import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.GlobalSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DeviceControlsTileTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var qsHost: QSHost
+ @Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ private lateinit var controlsComponent: ControlsComponent
+ @Mock
+ private lateinit var controlsUiController: ControlsUiController
+ @Mock
+ private lateinit var controlsListingController: ControlsListingController
+ @Mock
+ private lateinit var controlsController: ControlsController
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var controlsDialog: ControlsDialog
+ private lateinit var globalSettings: GlobalSettings
+ @Mock
+ private lateinit var serviceInfo: ControlsServiceInfo
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Captor
+ private lateinit var listingCallbackCaptor:
+ ArgumentCaptor<ControlsListingController.ControlsListingCallback>
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: DeviceControlsTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ `when`(qsHost.context).thenReturn(mContext)
+ `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ controlsComponent = ControlsComponent(
+ true,
+ { controlsController },
+ { controlsUiController },
+ { controlsListingController }
+ )
+
+ globalSettings = FakeSettings()
+
+ globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 1)
+ `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(true)
+
+ tile = createTile()
+ }
+
+ @Test
+ fun testAvailable() {
+ assertThat(tile.isAvailable).isTrue()
+ }
+
+ @Test
+ fun testNotAvailableFeature() {
+ `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(false)
+
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testNotAvailableControls() {
+ controlsComponent = ControlsComponent(
+ false,
+ { controlsController },
+ { controlsUiController },
+ { controlsListingController }
+ )
+ tile = createTile()
+
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testNotAvailableFlag() {
+ globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 0)
+ tile = createTile()
+
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testObservingCallback() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ any(ControlsListingController.ControlsListingCallback::class.java)
+ )
+ }
+
+ @Test
+ fun testLongClickIntent() {
+ assertThat(tile.longClickIntent.action).isEqualTo(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
+ }
+
+ @Test
+ fun testUnavailableByDefault() {
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun testStateUnavailableIfNoListings() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(emptyList())
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun testStateAvailableIfListings() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ }
+
+ @Test
+ fun testMoveBetweenStates() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ listingCallbackCaptor.value.onServicesUpdated(emptyList())
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun testNoDialogWhenUnavailable() {
+ tile.click()
+ testableLooper.processAllMessages()
+
+ verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
+ }
+
+ @Test
+ fun testDialogShowWhenAvailable() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ tile.click()
+ testableLooper.processAllMessages()
+
+ verify(controlsDialog).show(controlsUiController)
+ }
+
+ @Test
+ fun testDialogDismissedOnDestroy() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ tile.click()
+ testableLooper.processAllMessages()
+
+ tile.destroy()
+ testableLooper.processAllMessages()
+ verify(controlsDialog).dismiss()
+ }
+
+ private fun createTile(): DeviceControlsTile {
+ return DeviceControlsTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ controlsComponent,
+ featureFlags,
+ { controlsDialog },
+ globalSettings
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index ee1d758e7ae2..4162884680a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -393,10 +393,11 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private void positionClock() {
mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
- mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY,
- mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
- 0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */,
- mQsExpansion, mCutoutTopInset);
+ mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight,
+ 0 /* keyguardUserSwitcherHeight */, mPreferredClockY, mHasCustomClock,
+ mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
+ 0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */, mQsExpansion,
+ mCutoutTopInset);
mClockPositionAlgorithm.run(mClockPosition);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index c07ba723ab43..d0e70310810e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -37,6 +37,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
import android.os.PowerManager;
+import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.DisplayMetrics;
@@ -58,6 +59,7 @@ import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
@@ -68,7 +70,6 @@ import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
-import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -193,6 +194,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@Mock
+ private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponent;
+ @Mock
private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock
private KeyguardClockSwitchController mKeyguardClockSwitchController;
@@ -216,6 +219,9 @@ public class NotificationPanelViewTest extends SysuiTestCase {
private NotificationsQuickSettingsContainer mNotificationContainerParent;
@Mock
private AmbientState mAmbientState;
+ @Mock
+ private UserManager mUserManager;
+
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -299,11 +305,12 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mBiometricUnlockController, mStatusBarKeyguardViewManager,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
+ mKeyguardUserSwitcherComponent,
mGroupManager,
mNotificationAreaController,
mAuthController,
- new QSDetailDisplayer(),
mScrimController,
+ mUserManager,
mMediaDataManager,
mAmbientState,
mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
index fc1a79105db1..e479882ac50a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
@@ -60,9 +60,9 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
@Mock
private lateinit var layoutInflater: LayoutInflater
@Mock
- private lateinit var keyguardUserSwitcher: KeyguardUserSwitcher
+ private lateinit var keyguardUserSwitcherController: KeyguardUserSwitcherController
- private lateinit var adapter: KeyguardUserSwitcher.KeyguardUserAdapter
+ private lateinit var adapter: KeyguardUserSwitcherController.KeyguardUserAdapter
private lateinit var picture: Bitmap
@Before
@@ -72,8 +72,11 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, layoutInflater)
`when`(layoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
.thenReturn(inflatedUserDetailItemView)
- adapter = KeyguardUserSwitcher.KeyguardUserAdapter(mContext, userSwitcherController,
- keyguardUserSwitcher)
+ adapter = KeyguardUserSwitcherController.KeyguardUserAdapter(
+ mContext,
+ mContext.resources,
+ LayoutInflater.from(mContext),
+ userSwitcherController, keyguardUserSwitcherController)
picture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
}
@@ -118,11 +121,11 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
}
@Test
- fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsSameType() {
+ fun shouldSetOnOnClickListener_currentUser_notGuestUser_oldViewIsSameType() {
val v: UserDetailItemView? = createViewFromSameType(
isCurrentUser = true, isGuestUser = false)
assertNotNull(v)
- verify(v)!!.setOnClickListener(null)
+ verify(v)!!.setOnClickListener(adapter)
}
@Test
@@ -150,11 +153,11 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
}
@Test
- fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsDifferentType() {
+ fun shouldSetOnOnClickListener_currentUser_notGuestUser_oldViewIsDifferentType() {
val v: UserDetailItemView? = createViewFromDifferentType(
isCurrentUser = true, isGuestUser = false)
assertNotNull(v)
- verify(v)!!.setOnClickListener(null)
+ verify(v)!!.setOnClickListener(adapter)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 76269dda8245..f1fc0b7723fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -89,8 +89,6 @@ import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -297,7 +295,7 @@ public class BubblesTest extends SysuiTestCase {
mBubblesManager = new BubblesManager(
mContext,
- mBubbleController.getImpl(),
+ mBubbleController.asBubbles(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 5340ff7e967c..9e10b21ce3b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -261,7 +261,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mBubblesManager = new BubblesManager(
mContext,
- mBubbleController.getImpl(),
+ mBubbleController.asBubbles(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 6c30999f63a4..38275f7cd348 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1245,10 +1245,9 @@ public class BackupManagerService extends IBackupManager.Stub {
@Override
public IRestoreSession beginRestoreSessionForUser(
- int userId, String packageName, String transportID,
- @OperationType int operationType) throws RemoteException {
+ int userId, String packageName, String transportID) throws RemoteException {
return isUserReadyForBackup(userId)
- ? beginRestoreSession(userId, packageName, transportID, operationType) : null;
+ ? beginRestoreSession(userId, packageName, transportID) : null;
}
/**
@@ -1257,15 +1256,13 @@ public class BackupManagerService extends IBackupManager.Stub {
*/
@Nullable
public IRestoreSession beginRestoreSession(
- @UserIdInt int userId, String packageName, String transportName,
- @OperationType int operationType) {
+ @UserIdInt int userId, String packageName, String transportName) {
UserBackupManagerService userBackupManagerService =
getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
return userBackupManagerService == null
? null
- : userBackupManagerService.beginRestoreSession(packageName, transportName,
- operationType);
+ : userBackupManagerService.beginRestoreSession(packageName, transportName);
}
@Override
@@ -1350,15 +1347,15 @@ public class BackupManagerService extends IBackupManager.Stub {
if (!isUserReadyForBackup(userId)) {
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
- return requestBackup(userId, packages, observer, monitor, flags, OperationType.BACKUP);
+ return requestBackup(userId, packages, observer, monitor, flags);
}
@Override
public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags, @OperationType int operationType)
+ IBackupManagerMonitor monitor, int flags)
throws RemoteException {
return requestBackup(binderGetCallingUserId(), packages,
- observer, monitor, flags, operationType);
+ observer, monitor, flags);
}
/**
@@ -1370,15 +1367,13 @@ public class BackupManagerService extends IBackupManager.Stub {
String[] packages,
IBackupObserver observer,
IBackupManagerMonitor monitor,
- int flags,
- @OperationType int operationType) {
+ int flags) {
UserBackupManagerService userBackupManagerService =
getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
return userBackupManagerService == null
? BackupManager.ERROR_BACKUP_NOT_ALLOWED
- : userBackupManagerService.requestBackup(packages, observer, monitor, flags,
- operationType);
+ : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
}
@Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 136cd22fad83..faec95f142eb 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -127,6 +127,7 @@ import com.android.server.backup.params.RestoreParams;
import com.android.server.backup.restore.ActiveRestoreSession;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
@@ -1860,19 +1861,10 @@ public class UserBackupManagerService {
/**
* Requests a backup for the inputted {@code packages} with a specified {@link
- * IBackupManagerMonitor}.
- */
- public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) {
- return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
- }
-
- /**
- * Requests a backup for the inputted {@code packages} with a specified {@link
* IBackupManagerMonitor} and {@link OperationType}.
*/
public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags, @OperationType int operationType) {
+ IBackupManagerMonitor monitor, int flags) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
if (packages == null || packages.length < 1) {
@@ -1903,13 +1895,16 @@ public class UserBackupManagerService {
final TransportClient transportClient;
final String transportDirName;
+ int operationType;
try {
transportDirName =
mTransportManager.getTransportDirName(
mTransportManager.getCurrentTransportName());
transportClient =
mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
- } catch (TransportNotRegisteredException e) {
+ operationType = getOperationTypeFromTransport(transportClient);
+ } catch (TransportNotRegisteredException | TransportNotAvailableException
+ | RemoteException e) {
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
@@ -4024,15 +4019,13 @@ public class UserBackupManagerService {
}
/** Hand off a restore session. */
- public IRestoreSession beginRestoreSession(String packageName, String transport,
- @OperationType int operationType) {
+ public IRestoreSession beginRestoreSession(String packageName, String transport) {
if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
mUserId,
- "beginRestoreSession: pkg=" + packageName + " transport=" + transport
- + "operationType=" + operationType));
+ "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
}
boolean needPermission = true;
@@ -4073,6 +4066,17 @@ public class UserBackupManagerService {
}
}
+ int operationType;
+ try {
+ operationType = getOperationTypeFromTransport(
+ mTransportManager.getTransportClientOrThrow(transport, /* caller */
+ "BMS.beginRestoreSession"));
+ } catch (TransportNotAvailableException | TransportNotRegisteredException
+ | RemoteException e) {
+ Slog.w(TAG, "Failed to get operation type from transport: " + e);
+ return null;
+ }
+
synchronized (this) {
if (mActiveRestoreSession != null) {
Slog.i(
@@ -4356,6 +4360,23 @@ public class UserBackupManagerService {
}
}
+ @VisibleForTesting
+ @OperationType int getOperationTypeFromTransport(TransportClient transportClient)
+ throws TransportNotAvailableException, RemoteException {
+ long oldCallingId = Binder.clearCallingIdentity();
+ try {
+ IBackupTransport transport = transportClient.connectOrThrow(
+ /* caller */ "BMS.getOperationTypeFromTransport");
+ if ((transport.getTransportFlags() & BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER) != 0) {
+ return OperationType.MIGRATION;
+ } else {
+ return OperationType.BACKUP;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldCallingId);
+ }
+ }
+
private static String addUserIdToLogMessage(int userId, String message) {
return "[UserID:" + userId + "] " + message;
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index a6cfae492db9..c6a8660d8797 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -2,7 +2,7 @@
per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java = file:/services/core/java/com/android/server/net/OWNERS
# Vibrator / Threads
-per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com
+per-file VibratorManagerService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com
# Zram writeback
per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 00d8b0f1bed4..d10cf4dd0505 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -30,6 +30,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.R;
@@ -147,14 +148,15 @@ public class PersistentDataBlockService extends SystemService {
private int getAllowedUid(int userHandle) {
String allowedPackage = mContext.getResources()
.getString(R.string.config_persistentDataPackageName);
- PackageManager pm = mContext.getPackageManager();
int allowedUid = -1;
- try {
- allowedUid = pm.getPackageUidAsUser(allowedPackage,
- PackageManager.MATCH_SYSTEM_ONLY, userHandle);
- } catch (PackageManager.NameNotFoundException e) {
- // not expected
- Slog.e(TAG, "not able to find package " + allowedPackage, e);
+ if (!TextUtils.isEmpty(allowedPackage)) {
+ try {
+ allowedUid = mContext.getPackageManager().getPackageUidAsUser(
+ allowedPackage, PackageManager.MATCH_SYSTEM_ONLY, userHandle);
+ } catch (PackageManager.NameNotFoundException e) {
+ // not expected
+ Slog.e(TAG, "not able to find package " + allowedPackage, e);
+ }
}
return allowedUid;
}
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index e7e5d67ff9f4..d264f8570cf2 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -18,9 +18,14 @@ package com.android.server;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.os.BatteryStats;
import android.os.Binder;
@@ -74,6 +79,7 @@ import java.util.function.Function;
/** System implementation of {@link IVibratorManagerService}. */
public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final String TAG = "VibratorManagerService";
+ private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
@@ -89,7 +95,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override
public void onStart() {
mService = new VibratorManagerService(getContext(), new Injector());
- publishBinderService("vibrator_manager", mService);
+ publishBinderService(Context.VIBRATOR_MANAGER_SERVICE, mService);
}
@Override
@@ -105,6 +111,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private final Object mLock = new Object();
private final Context mContext;
+ private final String mSystemUiPackage;
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
private final Handler mHandler;
@@ -128,6 +135,26 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private VibrationScaler mVibrationScaler;
private InputDeviceDelegate mInputDeviceDelegate;
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ synchronized (mLock) {
+ // When the system is entering a non-interactive state, we want
+ // to cancel vibrations in case a misbehaving app has abandoned
+ // them. However it may happen that the system is currently playing
+ // haptic feedback as part of the transition. So we don't cancel
+ // system vibrations.
+ if (mCurrentVibration != null
+ && !isSystemHapticFeedback(mCurrentVibration.getVibration())) {
+ mNextVibration = null;
+ mCurrentVibration.cancel();
+ }
+ }
+ }
+ }
+ };
+
static native long nativeInit(OnSyncedVibrationCompleteListener listener);
static native long nativeGetFinalizer();
@@ -155,6 +182,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
com.android.internal.R.integer.config_previousVibrationsDumpLimit);
mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
+ mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
+ .getSystemUiServiceComponent().getPackageName();
+
mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
@@ -184,6 +214,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
for (int i = 0; i < mVibrators.size(); i++) {
mVibrators.valueAt(i).off();
}
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ context.registerReceiver(mIntentReceiver, filter);
+
+ injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
}
/** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
@@ -371,7 +407,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibratorManagerRecords.record(mCurrentExternalVibration);
mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
- // TODO(b/167946816): set external control to false
+ setExternalControl(false);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -432,6 +468,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ private void setExternalControl(boolean externalControl) {
+ for (int i = 0; i < mVibrators.size(); i++) {
+ mVibrators.valueAt(i).setExternalControl(externalControl);
+ }
+ }
+
@GuardedBy("mLock")
private void updateAlwaysOnLocked(AlwaysOnVibration vib) {
for (int i = 0; i < vib.effects.size(); i++) {
@@ -507,6 +549,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@GuardedBy("mLock")
+ private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) {
+ vib.end(status);
+ mVibratorManagerRecords.record(vib);
+ }
+
+ @GuardedBy("mLock")
private void reportFinishedVibrationLocked(Vibration.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -827,6 +875,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean isSystemHapticFeedback(Vibration vib) {
+ if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
+ return false;
+ }
+ return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg);
+ }
+
@GuardedBy("mLock")
private void onAllVibratorsLocked(Consumer<VibratorController> consumer) {
for (int i = 0; i < mVibrators.size(); i++) {
@@ -859,6 +914,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibratorController.OnVibrationCompleteListener listener) {
return new VibratorController(vibratorId, listener);
}
+
+ void addService(String name, IBinder service) {
+ ServiceManager.addService(name, service);
+ }
}
/**
@@ -1088,14 +1147,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
pw.println();
pw.println(" mCurrentVibration:");
- pw.println(" " + mCurrentVibration == null
- ? null : mCurrentVibration.getVibration().getDebugInfo());
+ pw.println(" " + (mCurrentVibration == null
+ ? null : mCurrentVibration.getVibration().getDebugInfo()));
pw.println(" mNextVibration:");
- pw.println(" " + mNextVibration == null
- ? null : mNextVibration.getVibration().getDebugInfo());
+ pw.println(" " + (mNextVibration == null
+ ? null : mNextVibration.getVibration().getDebugInfo()));
pw.println(" mCurrentExternalVibration:");
- pw.println(" " + mCurrentExternalVibration == null
- ? null : mCurrentExternalVibration.getDebugInfo());
+ pw.println(" " + (mCurrentExternalVibration == null
+ ? null : mCurrentExternalVibration.getDebugInfo()));
pw.println();
pw.println(" mVibrationSettings=" + mVibrationSettings);
for (int i = 0; i < mPreviousVibrations.size(); i++) {
@@ -1168,6 +1227,145 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
+ private final class ExternalVibratorService extends IExternalVibratorService.Stub {
+ ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;
+
+ @Override
+ public int onExternalVibrationStart(ExternalVibration vib) {
+ if (!hasExternalControlCapability()) {
+ return IExternalVibratorService.SCALE_MUTE;
+ }
+ if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
+ vib.getUid(), -1 /*owningUid*/, true /*exported*/)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
+ + " tried to play externally controlled vibration"
+ + " without VIBRATE permission, ignoring.");
+ return IExternalVibratorService.SCALE_MUTE;
+ }
+
+ int mode = checkAppOpModeLocked(vib.getUid(), vib.getPackage(),
+ vib.getVibrationAttributes());
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
+ vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
+ if (mode == AppOpsManager.MODE_ERRORED) {
+ Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
+ endVibrationLocked(vibHolder, Vibration.Status.IGNORED_ERROR_APP_OPS);
+ } else {
+ endVibrationLocked(vibHolder, Vibration.Status.IGNORED_APP_OPS);
+ }
+ return vibHolder.scale;
+ }
+
+ VibrationThread cancelingVibration = null;
+ int scale;
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null
+ && mCurrentExternalVibration.externalVibration.equals(vib)) {
+ // We are already playing this external vibration, so we can return the same
+ // scale calculated in the previous call to this method.
+ return mCurrentExternalVibration.scale;
+ }
+ if (mCurrentExternalVibration == null) {
+ // If we're not under external control right now, then cancel any normal
+ // vibration that may be playing and ready the vibrator for external control.
+ if (mCurrentVibration != null) {
+ mNextVibration = null;
+ mCurrentVibration.cancel();
+ cancelingVibration = mCurrentVibration;
+ }
+ } else {
+ endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
+ }
+ // At this point we either have an externally controlled vibration playing, or
+ // no vibration playing. Since the interface defines that only one externally
+ // controlled vibration can play at a time, by returning something other than
+ // SCALE_MUTE from this function we can be assured that if we are currently
+ // playing vibration, it will be muted in favor of the new vibration.
+ //
+ // Note that this doesn't support multiple concurrent external controls, as we
+ // would need to mute the old one still if it came from a different controller.
+ mCurrentExternalVibration = new ExternalVibrationHolder(vib);
+ mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
+ vib.linkToDeath(mCurrentExternalDeathRecipient);
+ mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
+ vib.getVibrationAttributes().getUsage());
+ scale = mCurrentExternalVibration.scale;
+ }
+
+ if (cancelingVibration != null) {
+ try {
+ cancelingVibration.join();
+ } catch (InterruptedException e) {
+ Slog.w("Interrupted while waiting for vibration to finish before starting "
+ + "external control", e);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator going under external control.");
+ }
+ setExternalControl(true);
+ if (DEBUG) {
+ Slog.e(TAG, "Playing external vibration: " + vib);
+ }
+ return scale;
+ }
+
+ @Override
+ public void onExternalVibrationStop(ExternalVibration vib) {
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null
+ && mCurrentExternalVibration.externalVibration.equals(vib)) {
+ if (DEBUG) {
+ Slog.e(TAG, "Stopping external vibration" + vib);
+ }
+ stopExternalVibrateLocked(Vibration.Status.FINISHED);
+ }
+ }
+ }
+
+ private void stopExternalVibrateLocked(Vibration.Status status) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "stopExternalVibrateLocked");
+ try {
+ if (mCurrentExternalVibration == null) {
+ return;
+ }
+ endVibrationLocked(mCurrentExternalVibration, status);
+ mCurrentExternalVibration.externalVibration.unlinkToDeath(
+ mCurrentExternalDeathRecipient);
+ mCurrentExternalDeathRecipient = null;
+ mCurrentExternalVibration = null;
+ setExternalControl(false);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private boolean hasExternalControlCapability() {
+ for (int i = 0; i < mVibrators.size(); i++) {
+ if (mVibrators.valueAt(i).hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
+ public void binderDied() {
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "External vibration finished because binder died");
+ }
+ stopExternalVibrateLocked(Vibration.Status.CANCELLED);
+ }
+ }
+ }
+ }
+ }
+
/** Provide limited functionality from {@link VibratorManagerService} as shell commands. */
private final class VibratorManagerShellCommand extends ShellCommand {
public static final String SHELL_PACKAGE_NAME = "com.android.shell";
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
deleted file mode 100644
index 2ac365d6d11b..000000000000
--- a/services/core/java/com/android/server/VibratorService.java
+++ /dev/null
@@ -1,1243 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.hardware.vibrator.IVibrator;
-import android.os.BatteryStats;
-import android.os.Binder;
-import android.os.CombinedVibrationEffect;
-import android.os.ExternalVibration;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IExternalVibratorService;
-import android.os.IVibratorService;
-import android.os.IVibratorStateListener;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.ShellCallback;
-import android.os.ShellCommand;
-import android.os.Trace;
-import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.os.VibratorInfo;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.DumpUtils;
-import com.android.server.vibrator.InputDeviceDelegate;
-import com.android.server.vibrator.Vibration;
-import com.android.server.vibrator.VibrationScaler;
-import com.android.server.vibrator.VibrationSettings;
-import com.android.server.vibrator.VibrationThread;
-import com.android.server.vibrator.VibratorController;
-import com.android.server.vibrator.VibratorController.OnVibrationCompleteListener;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/** System implementation of {@link IVibratorService}. */
-public class VibratorService extends IVibratorService.Stub {
- private static final String TAG = "VibratorService";
- private static final boolean DEBUG = false;
- private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
-
- // Default vibration attributes. Used when vibration is requested without attributes
- private static final VibrationAttributes DEFAULT_ATTRIBUTES =
- new VibrationAttributes.Builder().build();
-
- // Used to generate globally unique vibration ids.
- private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
-
- private final LinkedList<Vibration.DebugInfo> mPreviousRingVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousNotificationVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousAlarmVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousVibrations;
- private final int mPreviousVibrationsLimit;
- private final Handler mH;
- private final Object mLock = new Object();
- private final VibratorController mVibratorController;
- private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
-
- private final Context mContext;
- private final PowerManager.WakeLock mWakeLock;
- private final AppOpsManager mAppOps;
- private final IBatteryStats mBatteryStatsService;
- private final String mSystemUiPackage;
- private VibrationSettings mVibrationSettings;
- private VibrationScaler mVibrationScaler;
- private InputDeviceDelegate mInputDeviceDelegate;
-
- @GuardedBy("mLock")
- private VibrationThread mThread;
- @GuardedBy("mLock")
- private VibrationThread mNextVibrationThread;
-
- @GuardedBy("mLock")
- private Vibration mCurrentVibration;
- private int mCurVibUid = -1;
- private ExternalVibrationHolder mCurrentExternalVibration;
-
- /**
- * Implementation of {@link VibrationThread.VibrationCallbacks} that reports finished
- * vibrations.
- */
- private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
-
- @Override
- public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
- return false;
- }
-
- @Override
- public boolean triggerSyncedVibration(long vibrationId) {
- return false;
- }
-
- @Override
- public void cancelSyncedVibration() {
- }
-
- @Override
- public void onVibrationEnded(long vibrationId, Vibration.Status status) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration thread finished with status " + status);
- }
- synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
- mThread = null;
- reportFinishVibrationLocked(status);
- if (mNextVibrationThread != null) {
- startVibrationThreadLocked(mNextVibrationThread);
- mNextVibrationThread = null;
- }
- }
- }
- }
- }
-
- /**
- * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service.
- */
- private static final class VibrationCompleteListener implements OnVibrationCompleteListener {
- private WeakReference<VibratorService> mServiceRef;
-
- VibrationCompleteListener(VibratorService service) {
- mServiceRef = new WeakReference<>(service);
- }
-
- @Override
- public void onComplete(int vibratorId, long vibrationId) {
- VibratorService service = mServiceRef.get();
- if (service != null) {
- service.onVibrationComplete(vibratorId, vibrationId);
- }
- }
- }
-
- /** Holder for a {@link ExternalVibration}. */
- private final class ExternalVibrationHolder {
-
- public final ExternalVibration externalVibration;
- public int scale;
-
- private final long mStartTimeDebug;
- private long mEndTimeDebug;
- private Vibration.Status mStatus;
-
- private ExternalVibrationHolder(ExternalVibration externalVibration) {
- this.externalVibration = externalVibration;
- this.scale = IExternalVibratorService.SCALE_NONE;
- mStartTimeDebug = System.currentTimeMillis();
- mStatus = Vibration.Status.RUNNING;
- }
-
- public void end(Vibration.Status status) {
- if (mStatus != Vibration.Status.RUNNING) {
- // Vibration already ended, keep first ending status set and ignore this one.
- return;
- }
- mStatus = status;
- mEndTimeDebug = System.currentTimeMillis();
- }
-
- public Vibration.DebugInfo getDebugInfo() {
- return new Vibration.DebugInfo(
- mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
- scale, externalVibration.getVibrationAttributes(),
- externalVibration.getUid(), externalVibration.getPackage(),
- /* reason= */ null, mStatus);
- }
- }
-
- VibratorService(Context context) {
- this(context, new Injector());
- }
-
- @VisibleForTesting
- VibratorService(Context context, Injector injector) {
- mH = injector.createHandler(Looper.myLooper());
- mVibratorController = injector.createVibratorController(
- new VibrationCompleteListener(this));
-
- // Reset the hardware to a default state, in case this is a runtime
- // restart instead of a fresh boot.
- mVibratorController.off();
-
- mContext = context;
- PowerManager pm = context.getSystemService(PowerManager.class);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
- mWakeLock.setReferenceCounted(true);
-
- mAppOps = mContext.getSystemService(AppOpsManager.class);
- mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
- BatteryStats.SERVICE_NAME));
- mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
- .getSystemUiServiceComponent().getPackageName();
-
- mPreviousVibrationsLimit = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_previousVibrationsDumpLimit);
-
- mPreviousRingVibrations = new LinkedList<>();
- mPreviousNotificationVibrations = new LinkedList<>();
- mPreviousAlarmVibrations = new LinkedList<>();
- mPreviousVibrations = new LinkedList<>();
- mPreviousExternalVibrations = new LinkedList<>();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- context.registerReceiver(mIntentReceiver, filter);
-
- injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
- }
-
- public void systemReady() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
- try {
- mVibrationSettings = new VibrationSettings(mContext, mH);
- mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
- mInputDeviceDelegate = new InputDeviceDelegate(mContext, mH);
-
- mVibrationSettings.addListener(this::updateVibrators);
-
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mVibrationSettings.updateSettings();
- }
- }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
-
- updateVibrators();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- /** Callback for when vibration is complete, to be called by native. */
- @VisibleForTesting
- public void onVibrationComplete(int vibratorId, long vibrationId) {
- synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.id == vibrationId
- && mThread != null) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread");
- }
- // Let the thread playing the vibration handle the callback, since it might be
- // expecting the vibrator to turn off multiple times during a single vibration.
- mThread.vibratorComplete(vibratorId);
- }
- }
- }
-
- @Override // Binder call
- public boolean hasVibrator() {
- // For now, we choose to ignore the presence of input devices that have vibrators
- // when reporting whether the device has a vibrator. Applications often use this
- // information to decide whether to enable certain features so they expect the
- // result of hasVibrator() to be constant. For now, just report whether
- // the device has a built-in vibrator.
- return mVibratorController.isAvailable();
- }
-
- @Override // Binder call
- public boolean isVibrating() {
- if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
- throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
- }
- return mVibratorController.isVibrating();
- }
-
- @Override // Binder call
- public VibratorInfo getVibratorInfo() {
- return mVibratorController.getVibratorInfo();
- }
-
- @Override // Binder call
- public boolean registerVibratorStateListener(IVibratorStateListener listener) {
- if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
- throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
- }
- return mVibratorController.registerVibratorStateListener(listener);
- }
-
- @Override // Binder call
- @GuardedBy("mLock")
- public boolean unregisterVibratorStateListener(IVibratorStateListener listener) {
- if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
- throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
- }
- return mVibratorController.unregisterVibratorStateListener(listener);
- }
-
- @Override // Binder call
- public boolean hasAmplitudeControl() {
- // Input device vibrators always support amplitude controls.
- return mInputDeviceDelegate.isAvailable()
- || mVibratorController.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
- }
-
- private void verifyIncomingUid(int uid) {
- if (uid == Binder.getCallingUid()) {
- return;
- }
- if (Binder.getCallingPid() == Process.myPid()) {
- return;
- }
- mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
-
- /**
- * Validate the incoming VibrationEffect.
- *
- * We can't throw exceptions here since we might be called from some system_server component,
- * which would bring the whole system down.
- *
- * @return whether the VibrationEffect is valid
- */
- private static boolean verifyVibrationEffect(VibrationEffect effect) {
- if (effect == null) {
- // Effect must not be null.
- Slog.wtf(TAG, "effect must not be null");
- return false;
- }
- try {
- effect.validate();
- } catch (Exception e) {
- Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
- return false;
- }
- return true;
- }
-
- private VibrationEffect fixupVibrationEffect(VibrationEffect effect) {
- if (effect instanceof VibrationEffect.Prebaked
- && ((VibrationEffect.Prebaked) effect).shouldFallback()) {
- VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
- VibrationEffect fallback = mVibrationSettings.getFallbackEffect(prebaked.getId());
- return new VibrationEffect.Prebaked(prebaked.getId(), prebaked.getEffectStrength(),
- fallback);
- }
- return effect;
- }
-
- private VibrationAttributes fixupVibrationAttributes(VibrationAttributes attrs) {
- if (attrs == null) {
- attrs = DEFAULT_ATTRIBUTES;
- }
- if (shouldBypassDnd(attrs)) {
- if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
- final int flags = attrs.getFlags()
- & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
- attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(flags, attrs.getFlags()).build();
- }
- }
-
- return attrs;
- }
-
- @Override // Binder call
- public void vibrate(int uid, String opPkg, VibrationEffect effect,
- @Nullable VibrationAttributes attrs, String reason, IBinder token) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
- try {
- if (!hasPermission(android.Manifest.permission.VIBRATE)) {
- throw new SecurityException("Requires VIBRATE permission");
- }
- if (token == null) {
- Slog.e(TAG, "token must not be null");
- return;
- }
- verifyIncomingUid(uid);
- if (!verifyVibrationEffect(effect)) {
- return;
- }
- effect = fixupVibrationEffect(effect);
- attrs = fixupVibrationAttributes(attrs);
- Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(),
- CombinedVibrationEffect.createSynced(effect), attrs, uid, opPkg, reason);
-
- // If our current vibration is longer than the new vibration and is the same amplitude,
- // then just let the current one finish.
- synchronized (mLock) {
- VibrationEffect currentEffect =
- mCurrentVibration == null ? null : getEffect(mCurrentVibration);
- if (effect instanceof VibrationEffect.OneShot
- && currentEffect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
- VibrationEffect.OneShot currentOneShot =
- (VibrationEffect.OneShot) currentEffect;
- if (currentOneShot.getDuration() > newOneShot.getDuration()
- && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
- if (DEBUG) {
- Slog.d(TAG,
- "Ignoring incoming vibration in favor of current vibration");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_ONGOING);
- return;
- }
- }
-
-
- // If something has external control of the vibrator, assume that it's more
- // important for now.
- if (mCurrentExternalVibration != null) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_EXTERNAL);
- return;
- }
-
- // If the current vibration is repeating and the incoming one is non-repeating,
- // then ignore the non-repeating vibration. This is so that we don't cancel
- // vibrations that are meant to grab the attention of the user, like ringtones and
- // alarms, in favor of one-shot vibrations that are likely quite short.
- if (!isRepeatingVibration(effect)
- && mCurrentVibration != null
- && isRepeatingVibration(currentEffect)) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_ALARM);
- return;
- }
-
- if (!mVibrationSettings.shouldVibrateForUid(uid, vib.attrs.getUsage())) {
- Slog.e(TAG, "Ignoring incoming vibration as process with"
- + " uid= " + uid + " is background,"
- + " attrs= " + vib.attrs);
- endVibrationLocked(vib, Vibration.Status.IGNORED_BACKGROUND);
- return;
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- startVibrationLocked(vib);
- boolean isNextVibration = mNextVibrationThread != null
- && vib.equals(mNextVibrationThread.getVibration());
-
- if (!vib.hasEnded() && !vib.equals(mCurrentVibration) && !isNextVibration) {
- // Vibration was unexpectedly ignored: add to list for debugging
- endVibrationLocked(vib, Vibration.Status.IGNORED);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private boolean hasPermission(String permission) {
- return mContext.checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- private static boolean isRepeatingVibration(VibrationEffect effect) {
- return effect.getDuration() == Long.MAX_VALUE;
- }
-
- private static <T extends VibrationEffect> T getEffect(Vibration vib) {
- return (T) ((CombinedVibrationEffect.Mono) vib.getEffect()).getEffect();
- }
-
- private void endVibrationLocked(Vibration vib, Vibration.Status status) {
- final LinkedList<Vibration.DebugInfo> previousVibrations;
- switch (vib.attrs.getUsage()) {
- case VibrationAttributes.USAGE_NOTIFICATION:
- previousVibrations = mPreviousNotificationVibrations;
- break;
- case VibrationAttributes.USAGE_RINGTONE:
- previousVibrations = mPreviousRingVibrations;
- break;
- case VibrationAttributes.USAGE_ALARM:
- previousVibrations = mPreviousAlarmVibrations;
- break;
- default:
- previousVibrations = mPreviousVibrations;
- }
- if (previousVibrations.size() > mPreviousVibrationsLimit) {
- previousVibrations.removeFirst();
- }
- vib.end(status);
- previousVibrations.addLast(vib.getDebugInfo());
- }
-
- private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) {
- if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
- mPreviousExternalVibrations.removeFirst();
- }
- vib.end(status);
- mPreviousExternalVibrations.addLast(vib.getDebugInfo());
- }
-
- @Override // Binder call
- public void cancelVibrate(IBinder token) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.VIBRATE,
- "cancelVibrate");
-
- synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.token == token) {
- if (DEBUG) {
- Slog.d(TAG, "Canceling vibration.");
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mNextVibrationThread = null;
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
- }
-
- @GuardedBy("mLock")
- private void doCancelVibrateLocked(Vibration.Status status) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
- try {
- if (mThread != null) {
- mThread.cancel();
- }
- mInputDeviceDelegate.cancelVibrateIfAvailable();
- if (mCurrentExternalVibration != null) {
- endVibrationLocked(mCurrentExternalVibration, status);
- mCurrentExternalVibration.externalVibration.mute();
- mCurrentExternalVibration = null;
- mVibratorController.setExternalControl(false);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @GuardedBy("mLock")
- private void startVibrationLocked(final Vibration vib) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
- try {
- if (!shouldVibrate(vib)) {
- return;
- }
- applyVibrationIntensityScalingLocked(vib);
- startVibrationInnerLocked(vib);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @GuardedBy("mLock")
- private void startVibrationInnerLocked(Vibration vib) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
- try {
- boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
- vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
- if (inputDevicesAvailable) {
- endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
- } else if (mThread == null) {
- startVibrationThreadLocked(new VibrationThread(vib, mVibratorController, mWakeLock,
- mBatteryStatsService, mVibrationCallbacks));
- } else {
- mNextVibrationThread = new VibrationThread(vib, mVibratorController, mWakeLock,
- mBatteryStatsService, mVibrationCallbacks);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @GuardedBy("mLock")
- private void startVibrationThreadLocked(VibrationThread thread) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
- mCurrentVibration = thread.getVibration();
- mThread = thread;
- mThread.start();
- }
-
- /** Scale the vibration effect by the intensity as appropriate based its intent. */
- private void applyVibrationIntensityScalingLocked(Vibration vib) {
- vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
- }
-
- private static boolean shouldBypassDnd(VibrationAttributes attrs) {
- return attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
- }
-
- private int getAppOpMode(int uid, String packageName, VibrationAttributes attrs) {
- int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
- attrs.getAudioUsage(), uid, packageName);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, packageName);
- }
-
- if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(attrs)) {
- // If we're just ignoring the vibration op then this is set by DND and we should ignore
- // if we're asked to bypass. AppOps won't be able to record this operation, so make
- // sure we at least note it in the logs for debugging.
- Slog.d(TAG, "Bypassing DND for vibrate from uid " + uid);
- mode = AppOpsManager.MODE_ALLOWED;
- }
- return mode;
- }
-
- private boolean shouldVibrate(Vibration vib) {
- if (!mVibrationSettings.shouldVibrateForPowerMode(vib.attrs.getUsage())) {
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_POWER);
- return false;
- }
-
- int intensity = mVibrationSettings.getCurrentIntensity(vib.attrs.getUsage());
- if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_SETTINGS);
- return false;
- }
-
- if (!mVibrationSettings.shouldVibrateForRingerMode(vib.attrs.getUsage())) {
- if (DEBUG) {
- Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_RINGTONE);
- return false;
- }
-
- final int mode = getAppOpMode(vib.uid, vib.opPkg, vib.attrs);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- if (mode == AppOpsManager.MODE_ERRORED) {
- // We might be getting calls from within system_server, so we don't actually
- // want to throw a SecurityException here.
- Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
- endVibrationLocked(vib, Vibration.Status.IGNORED_ERROR_APP_OPS);
- } else {
- endVibrationLocked(vib, Vibration.Status.IGNORED_APP_OPS);
- }
- return false;
- }
-
- return true;
- }
-
- @GuardedBy("mLock")
- private void reportFinishVibrationLocked(Vibration.Status status) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
- try {
- if (mCurrentVibration != null) {
- endVibrationLocked(mCurrentVibration, status);
- mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
- mCurrentVibration.opPkg);
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
- mCurrentVibration = null;
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @VisibleForTesting
- void updateVibrators() {
- synchronized (mLock) {
- boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators(
- mVibrationSettings.shouldVibrateInputDevices());
-
- if (mCurrentVibration == null) {
- return;
- }
-
- if (inputDevicesChanged || !mVibrationSettings.shouldVibrateForPowerMode(
- mCurrentVibration.attrs.getUsage())) {
- // If the state changes out from under us then just reset.
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
-
- private boolean isSystemHapticFeedback(Vibration vib) {
- if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
- return false;
- }
- return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg);
- }
-
- private void dumpInternal(PrintWriter pw) {
- pw.println("Vibrator Service:");
- synchronized (mLock) {
- pw.print(" mCurrentVibration=");
- if (mCurrentVibration != null) {
- pw.println(mCurrentVibration.getDebugInfo().toString());
- } else {
- pw.println("null");
- }
- pw.print(" mCurrentExternalVibration=");
- if (mCurrentExternalVibration != null) {
- pw.println(mCurrentExternalVibration.getDebugInfo().toString());
- } else {
- pw.println("null");
- }
- pw.println(" mVibratorController=" + mVibratorController);
- pw.println(" mVibrationSettings=" + mVibrationSettings);
- pw.println();
- pw.println(" Previous ring vibrations:");
- for (Vibration.DebugInfo info : mPreviousRingVibrations) {
- pw.print(" ");
- pw.println(info.toString());
- }
-
- pw.println(" Previous notification vibrations:");
- for (Vibration.DebugInfo info : mPreviousNotificationVibrations) {
- pw.println(" " + info);
- }
-
- pw.println(" Previous alarm vibrations:");
- for (Vibration.DebugInfo info : mPreviousAlarmVibrations) {
- pw.println(" " + info);
- }
-
- pw.println(" Previous vibrations:");
- for (Vibration.DebugInfo info : mPreviousVibrations) {
- pw.println(" " + info);
- }
-
- pw.println(" Previous external vibrations:");
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- pw.println(" " + info);
- }
- }
- }
-
- private void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
-
- synchronized (mLock) {
- if (mCurrentVibration != null) {
- mCurrentVibration.getDebugInfo().dumpProto(proto,
- VibratorServiceDumpProto.CURRENT_VIBRATION);
- }
- if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
- VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
- }
- proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating());
- proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
- mVibratorController.isUnderExternalControl());
- mVibrationSettings.dumpProto(proto);
-
- for (Vibration.DebugInfo info : mPreviousRingVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousNotificationVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousAlarmVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
- }
- }
- proto.flush();
- }
-
- /** Point of injection for test dependencies */
- @VisibleForTesting
- static class Injector {
-
- VibratorController createVibratorController(OnVibrationCompleteListener listener) {
- return new VibratorController(/* vibratorId= */ -1, listener);
- }
-
- Handler createHandler(Looper looper) {
- return new Handler(looper);
- }
-
- void addService(String name, IBinder service) {
- ServiceManager.addService(name, service);
- }
- }
-
- BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- synchronized (mLock) {
- // When the system is entering a non-interactive state, we want
- // to cancel vibrations in case a misbehaving app has abandoned
- // them. However it may happen that the system is currently playing
- // haptic feedback as part of the transition. So we don't cancel
- // system vibrations.
- if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) {
- mNextVibrationThread = null;
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
- }
- };
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
- final long ident = Binder.clearCallingIdentity();
-
- boolean isDumpProto = false;
- for (String arg : args) {
- if (arg.equals("--proto")) {
- isDumpProto = true;
- }
- }
- try {
- if (isDumpProto) {
- dumpProto(fd);
- } else {
- dumpInternal(pw);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
- new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
- }
-
- final class ExternalVibratorService extends IExternalVibratorService.Stub {
- ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;
-
- @Override
- public int onExternalVibrationStart(ExternalVibration vib) {
- if (!mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
- return IExternalVibratorService.SCALE_MUTE;
- }
- if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
- vib.getUid(), -1 /*owningUid*/, true /*exported*/)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
- + " tried to play externally controlled vibration"
- + " without VIBRATE permission, ignoring.");
- return IExternalVibratorService.SCALE_MUTE;
- }
-
- int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
- if (mode != AppOpsManager.MODE_ALLOWED) {
- ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
- vibHolder.scale = SCALE_MUTE;
- if (mode == AppOpsManager.MODE_ERRORED) {
- Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
- endVibrationLocked(vibHolder, Vibration.Status.IGNORED_ERROR_APP_OPS);
- } else {
- endVibrationLocked(vibHolder, Vibration.Status.IGNORED_APP_OPS);
- }
- return IExternalVibratorService.SCALE_MUTE;
- }
-
- VibrationThread cancelingVibration = null;
- int scale;
- synchronized (mLock) {
- if (mCurrentExternalVibration != null
- && mCurrentExternalVibration.externalVibration.equals(vib)) {
- // We are already playing this external vibration, so we can return the same
- // scale calculated in the previous call to this method.
- return mCurrentExternalVibration.scale;
- }
- if (mCurrentExternalVibration == null) {
- // If we're not under external control right now, then cancel any normal
- // vibration that may be playing and ready the vibrator for external control.
- mNextVibrationThread = null;
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- cancelingVibration = mThread;
- } else {
- endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
- }
- // At this point we either have an externally controlled vibration playing, or
- // no vibration playing. Since the interface defines that only one externally
- // controlled vibration can play at a time, by returning something other than
- // SCALE_MUTE from this function we can be assured that if we are currently
- // playing vibration, it will be muted in favor of the new vibration.
- //
- // Note that this doesn't support multiple concurrent external controls, as we
- // would need to mute the old one still if it came from a different controller.
- mCurrentExternalVibration = new ExternalVibrationHolder(vib);
- mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
- vib.linkToDeath(mCurrentExternalDeathRecipient);
- mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
- vib.getVibrationAttributes().getUsage());
- scale = mCurrentExternalVibration.scale;
- }
- if (cancelingVibration != null) {
- try {
- cancelingVibration.join();
- } catch (InterruptedException e) {
- Slog.w("Interrupted while waiting current vibration to be cancelled before "
- + "starting external vibration", e);
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "Vibrator going under external control.");
- }
- mVibratorController.setExternalControl(true);
- if (DEBUG) {
- Slog.e(TAG, "Playing external vibration: " + vib);
- }
- return scale;
- }
-
- @Override
- public void onExternalVibrationStop(ExternalVibration vib) {
- synchronized (mLock) {
- if (mCurrentExternalVibration != null
- && mCurrentExternalVibration.externalVibration.equals(vib)) {
- if (DEBUG) {
- Slog.e(TAG, "Stopping external vibration" + vib);
- }
- doCancelExternalVibrateLocked(Vibration.Status.FINISHED);
- }
- }
- }
-
- private void doCancelExternalVibrateLocked(Vibration.Status status) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelExternalVibrateLocked");
- try {
- if (mCurrentExternalVibration == null) {
- return;
- }
- endVibrationLocked(mCurrentExternalVibration, status);
- mCurrentExternalVibration.externalVibration.unlinkToDeath(
- mCurrentExternalDeathRecipient);
- mCurrentExternalDeathRecipient = null;
- mCurrentExternalVibration = null;
- mVibratorController.setExternalControl(false);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
- public void binderDied() {
- synchronized (mLock) {
- if (mCurrentExternalVibration != null) {
- if (DEBUG) {
- Slog.d(TAG, "External vibration finished because binder died");
- }
- doCancelExternalVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
- }
- }
-
- private final class VibratorShellCommand extends ShellCommand {
-
- private final IBinder mToken;
-
- private final class CommonOptions {
- public boolean force = false;
- public void check(String opt) {
- switch (opt) {
- case "-f":
- force = true;
- break;
- }
- }
- }
-
- private VibratorShellCommand(IBinder token) {
- mToken = token;
- }
-
- @Override
- public int onCommand(String cmd) {
- if ("vibrate".equals(cmd)) {
- return runVibrate();
- } else if ("waveform".equals(cmd)) {
- return runWaveform();
- } else if ("prebaked".equals(cmd)) {
- return runPrebaked();
- } else if ("capabilities".equals(cmd)) {
- return runCapabilities();
- } else if ("cancel".equals(cmd)) {
- cancelVibrate(mToken);
- return 0;
- }
- return handleDefaultCommands(cmd);
- }
-
- private boolean checkDoNotDisturb(CommonOptions opts) {
- if (mVibrationSettings.isInZenMode() && !opts.force) {
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.print("Ignoring because device is on DND mode ");
- return true;
- }
- }
- return false;
- }
-
- private int runVibrate() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
- try {
- CommonOptions commonOptions = new CommonOptions();
-
- String opt;
- while ((opt = getNextOption()) != null) {
- commonOptions.check(opt);
- }
-
- if (checkDoNotDisturb(commonOptions)) {
- return 0;
- }
-
- final long duration = Long.parseLong(getNextArgRequired());
- String description = getNextArg();
- if (description == null) {
- description = "Shell command";
- }
-
- VibrationEffect effect =
- VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
- mToken);
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private int runWaveform() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runWaveform");
- try {
- String description = "Shell command";
- int repeat = -1;
- ArrayList<Integer> amplitudesList = null;
- CommonOptions commonOptions = new CommonOptions();
-
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "-d":
- description = getNextArgRequired();
- break;
- case "-r":
- repeat = Integer.parseInt(getNextArgRequired());
- break;
- case "-a":
- if (amplitudesList == null) {
- amplitudesList = new ArrayList<Integer>();
- }
- break;
- default:
- commonOptions.check(opt);
- break;
- }
- }
-
- if (checkDoNotDisturb(commonOptions)) {
- return 0;
- }
-
- ArrayList<Long> timingsList = new ArrayList<Long>();
-
- String arg;
- while ((arg = getNextArg()) != null) {
- if (amplitudesList != null && amplitudesList.size() < timingsList.size()) {
- amplitudesList.add(Integer.parseInt(arg));
- } else {
- timingsList.add(Long.parseLong(arg));
- }
- }
-
- VibrationEffect effect;
- long[] timings = timingsList.stream().mapToLong(Long::longValue).toArray();
- if (amplitudesList == null) {
- effect = VibrationEffect.createWaveform(timings, repeat);
- } else {
- int[] amplitudes =
- amplitudesList.stream().mapToInt(Integer::intValue).toArray();
- effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
- }
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
- mToken);
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private int runPrebaked() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
- try {
- CommonOptions commonOptions = new CommonOptions();
- boolean shouldFallback = false;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- if ("-b".equals(opt)) {
- shouldFallback = true;
- } else {
- commonOptions.check(opt);
- }
- }
-
- if (checkDoNotDisturb(commonOptions)) {
- return 0;
- }
-
- final int id = Integer.parseInt(getNextArgRequired());
-
- String description = getNextArg();
- if (description == null) {
- description = "Shell command";
- }
-
- VibrationEffect effect = VibrationEffect.get(id, shouldFallback);
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
- mToken);
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private int runCapabilities() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runCapabilities");
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.println("Vibrator capabilities:");
- if (mVibratorController.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
- pw.println(" Always on effects");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
- pw.println(" Compose effects");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
- pw.println(" Amplitude control");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
- pw.println(" External control");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
- pw.println(" External amplitude control");
- }
- pw.println("");
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
- final int flags = commonOptions.force
- ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
- : 0;
- return new VibrationAttributes.Builder()
- .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
- // Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects.
- .setUsage(VibrationAttributes.USAGE_TOUCH)
- .build();
- }
-
- @Override
- public void onHelp() {
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.println("Vibrator commands:");
- pw.println(" help");
- pw.println(" Prints this help text.");
- pw.println("");
- pw.println(" vibrate duration [description]");
- pw.println(" Vibrates for duration milliseconds; ignored when device is on ");
- pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting ");
- pw.println(" will be used to scale amplitude.");
- pw.println(" waveform [-d description] [-r index] [-a] duration [amplitude] ...");
- pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
- pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
- pw.println(" user setting will be used to scale amplitude.");
- pw.println(" If -r is provided, the waveform loops back to the specified");
- pw.println(" index (e.g. 0 loops from the beginning)");
- pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
- pw.println(" otherwise, it accepts durations only and alternates off/on");
- pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
- pw.println(" prebaked [-b] effect-id [description]");
- pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
- pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
- pw.println(" will be used to scale amplitude.");
- pw.println(" If -b is provided, the prebaked fallback effect will be played if");
- pw.println(" the device doesn't support the given effect-id.");
- pw.println(" capabilities");
- pw.println(" Prints capabilities of this device.");
- pw.println(" cancel");
- pw.println(" Cancels any active vibration");
- pw.println("Common Options:");
- pw.println(" -f - Force. Ignore Do Not Disturb setting.");
- pw.println("");
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index c6947c2d9525..b9943897a486 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -15,8 +15,6 @@
*/
package com.android.server.am;
-import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
-
import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
@@ -39,13 +37,12 @@ import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.IntArray;
import android.util.Slog;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.internal.power.MeasuredEnergyArray;
import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -148,13 +145,13 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
private WifiActivityEnergyInfo mLastWifiInfo =
new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
- /** Maps the EnergyConsumer id to it's corresponding {@link MeasuredEnergySubsystem} */
- @GuardedBy("mWorkerLock")
- private @Nullable SparseIntArray mEnergyConsumerToSubsystemMap = null;
-
- /** Maps a {@link MeasuredEnergySubsystem} to it's corresponding EnergyConsumer id */
+ /**
+ * Maps an {@link EnergyConsumerType} to it's corresponding {@link EnergyConsumer#id}s,
+ * unless it is of {@link EnergyConsumer#type}=={@link EnergyConsumerType#OTHER}
+ */
+ // TODO: Hook this up (it isn't used yet)
@GuardedBy("mWorkerLock")
- private @Nullable SparseIntArray mSubsystemToEnergyConsumerMap = null;
+ private @Nullable SparseArray<int[]> mEnergyConsumerTypeToIdMap = null;
/** Snapshot of measured energies, or null if no measured energies are supported. */
@GuardedBy("mWorkerLock")
@@ -204,18 +201,26 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
mWifiManager = wm;
mTelephony = tm;
mPowerStatsInternal = psi;
+
+ boolean[] supportedStdBuckets = null;
+ int numCustomBuckets = 0;
if (mPowerStatsInternal != null) {
- populateEnergyConsumerSubsystemMapsLocked();
- final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData();
- mMeasuredEnergySnapshot = initialMeasuredEnergies == null
- ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
- final boolean[] supportedStdBuckets
- = getSupportedEnergyBuckets(initialMeasuredEnergies);
- final int numCustomBuckets = 0; // TODO: Get this from initialMeasuredEnergies
- synchronized (mStats) {
- mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets);
+ final SparseArray<EnergyConsumer> idToConsumer
+ = populateEnergyConsumerSubsystemMapsLocked();
+ if (idToConsumer != null) {
+ mMeasuredEnergySnapshot = new MeasuredEnergySnapshot(idToConsumer);
+ final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData();
+ // According to spec, initialEcrs will include 0s for consumers that haven't
+ // used any energy yet, as long as they are supported; however, attributed uid
+ // energies will be absent if their energy is 0.
+ mMeasuredEnergySnapshot.updateAndGetDelta(initialEcrs);
+ numCustomBuckets = mMeasuredEnergySnapshot.getNumOtherOrdinals();
+ supportedStdBuckets = getSupportedEnergyBuckets(idToConsumer);
}
}
+ synchronized (mStats) {
+ mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets);
+ }
}
}
@@ -568,7 +573,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
} catch (ExecutionException e) {
Slog.w(TAG, "exception reading modem stats: " + e.getCause());
}
- final SparseLongArray energyDeltas = mMeasuredEnergySnapshot == null ? null :
+
+ final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas =
+ mMeasuredEnergySnapshot == null ? null :
mMeasuredEnergySnapshot.updateAndGetDelta(getMeasuredEnergyLocked(updateFlags));
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -576,6 +583,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
final long elapsedRealtimeUs = elapsedRealtime * 1000;
final long uptimeUs = uptime * 1000;
+ // Now that we have finally received all the data, we can tell mStats about it.
synchronized (mStats) {
mStats.addHistoryEventLocked(
elapsedRealtime,
@@ -601,10 +609,21 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
// Inform mStats about each applicable measured energy.
- if (energyDeltas != null) {
- final long displayEnergy = energyDeltas.get(SUBSYSTEM_DISPLAY, 0L);
- // Always pass in what BatteryExternalStatsWorker thinks screenState is.
- mStats.updateDisplayEnergyLocked(displayEnergy, screenState, elapsedRealtime);
+ if (measuredEnergyDeltas != null) {
+ final long displayEnergy = measuredEnergyDeltas.displayEnergyUJ;
+ if (displayEnergy != MeasuredEnergySnapshot.UNAVAILABLE) {
+ // If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
+ mStats.updateDisplayEnergyLocked(displayEnergy, screenState, elapsedRealtime);
+ }
+ }
+ // Inform mStats about each applicable custom energy bucket.
+ if (measuredEnergyDeltas != null && measuredEnergyDeltas.otherTotalEnergyUJ != null) {
+ // Iterate over the custom (EnergyConsumerType.OTHER) ordinals.
+ for (int ord = 0; ord < measuredEnergyDeltas.otherTotalEnergyUJ.length; ord++) {
+ long totalEnergy = measuredEnergyDeltas.otherTotalEnergyUJ[ord];
+ SparseLongArray uidEnergies = measuredEnergyDeltas.otherUidEnergiesUJ[ord];
+ mStats.updateCustomMeasuredEnergyDataLocked(ord, totalEnergy, uidEnergies);
+ }
}
if (bluetoothInfo != null) {
@@ -621,7 +640,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if (wifiInfo != null) {
if (wifiInfo.isValid()) {
- // TODO: wifiEnergyDelta = energyDeltas.get(MeasuredEnergyArray.SUBSYSTEM_WIFI, 0L);
+ // TODO: wifiEnergyDelta = measuredEnergyDeltas.consumerTypeEnergyUJ
+ // .get(EnergyConsumerType.WIFI, MeasuredEnergySnapshot.UNAVAILABLE)
mStats.updateWifiState(extractDeltaLocked(wifiInfo)
/*, TODO: wifiEnergyDelta */, elapsedRealtime, uptime);
} else {
@@ -740,21 +760,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
/**
- * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to
+ * Map the {@link EnergyConsumerType}s in the given energyArray to
* their corresponding {@link MeasuredEnergyStats.StandardEnergyBucket}s.
* Does not include custom energy buckets (which are always, by definition, supported).
*
* @return array with true for index i if standard energy bucket i is supported.
*/
- private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
- if (energyArray == null) {
+ private static @Nullable boolean[] getSupportedEnergyBuckets(
+ SparseArray<EnergyConsumer> idToConsumer) {
+ if (idToConsumer == null) {
return null;
}
final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
- final int size = energyArray.size();
- for (int energyIdx = 0; energyIdx < size; energyIdx++) {
- switch (energyArray.getSubsystem(energyIdx)) {
- case MeasuredEnergyArray.SUBSYSTEM_DISPLAY:
+ final int size = idToConsumer.size();
+ for (int idx = 0; idx < size; idx++) {
+ final EnergyConsumer consumer = idToConsumer.valueAt(idx);
+ switch (consumer.type) {
+ case EnergyConsumerType.DISPLAY:
buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true;
buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true;
buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true;
@@ -764,71 +786,22 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
return buckets;
}
- /**
- * Get a {@link MeasuredEnergyArray} with the latest
- * {@link MeasuredEnergyArray.MeasuredEnergySubsystem} energy usage since boot.
- *
- * TODO(b/176988041): Replace {@link MeasuredEnergyArray} usage with {@link
- * EnergyConsumerResult}[]
- */
+ /** Get {@link EnergyConsumerResult}s with the latest energy usage since boot. */
@GuardedBy("mWorkerLock")
- @VisibleForTesting
- public @Nullable MeasuredEnergyArray getEnergyConsumptionData() {
- final EnergyConsumerResult[] results;
+ private @Nullable EnergyConsumerResult[] getEnergyConsumptionData() {
try {
- results = mPowerStatsInternal.getEnergyConsumedAsync(new int[0])
+ return mPowerStatsInternal.getEnergyConsumedAsync(new int[0])
.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Slog.e(TAG, "Failed to getEnergyConsumedAsync", e);
return null;
}
- if (results == null) return null;
- final int size = results.length;
- final int[] subsystems = new int[size];
- final long[] energyUJ = new long[size];
-
- int count = 0;
- for (int i = 0; i < size; i++) {
- final EnergyConsumerResult consumer = results[i];
- final int subsystem = mEnergyConsumerToSubsystemMap.get(consumer.id,
- MeasuredEnergyArray.SUBSYSTEM_UNKNOWN);
- if (subsystem == MeasuredEnergyArray.SUBSYSTEM_UNKNOWN) continue;
- subsystems[count] = subsystem;
- energyUJ[count] = consumer.energyUWs;
- count++;
- }
- final int arraySize = count;
- return new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- if (index >= size()) {
- throw new IllegalArgumentException(
- "Out of bounds subsystem index! index : " + index + ", size : "
- + size());
- }
- return subsystems[index];
- }
-
- @Override
- public long getEnergy(int index) {
- if (index >= size()) {
- throw new IllegalArgumentException(
- "Out of bounds subsystem index! index : " + index + ", size : "
- + size());
- }
- return energyUJ[index];
- }
-
- @Override
- public int size() {
- return arraySize;
- }
- };
}
- /** Fetch MeasuredEnergyArray for supported subsystems based on the given updateFlags. */
+ /** Fetch EnergyConsumerResult[] for supported subsystems based on the given updateFlags. */
@GuardedBy("mWorkerLock")
- private @Nullable MeasuredEnergyArray getMeasuredEnergyLocked(@ExternalUpdateFlag int flags) {
+ private @Nullable EnergyConsumerResult[] getMeasuredEnergyLocked(@ExternalUpdateFlag int flags)
+ {
if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null;
if (flags == UPDATE_ALL) {
@@ -836,12 +809,13 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
return getEnergyConsumptionData();
}
- final List<Integer> energySubsystems = new ArrayList<>();
+ final List<Integer> energyConsumerIds = new ArrayList<>();
if ((flags & UPDATE_DISPLAY) != 0) {
- addEnergyConsumerIdLocked(energySubsystems, SUBSYSTEM_DISPLAY);
+ addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY);
}
// TODO: Wifi, Bluetooth, etc., go here
- if (energySubsystems.isEmpty()) {
+
+ if (energyConsumerIds.isEmpty()) {
return null;
}
// TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray()
@@ -849,59 +823,48 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
@GuardedBy("mWorkerLock")
- private void addEnergyConsumerIdLocked(List<Integer> energyConsumerIds,
- @MeasuredEnergyArray.MeasuredEnergySubsystem int consumerId) {
- if (mMeasuredEnergySnapshot.hasSubsystem(consumerId)) {
- energyConsumerIds.add(consumerId);
- }
+ private void addEnergyConsumerIdLocked(
+ List<Integer> energyConsumerIds, @EnergyConsumerType int type) {
+ final int consumerId = 0; // TODO: Use mEnergyConsumerTypeToIdMap to get this
+ energyConsumerIds.add(consumerId);
}
+ /** Populates the cached type->ids map, and returns the (inverse) id->EnergyConsumer map. */
@GuardedBy("mWorkerLock")
- private void populateEnergyConsumerSubsystemMapsLocked() {
+ private @Nullable SparseArray<EnergyConsumer> populateEnergyConsumerSubsystemMapsLocked() {
if (mPowerStatsInternal == null) {
- // PowerStatsInternal unavailable, don't bother populating maps.
- mEnergyConsumerToSubsystemMap = null;
- mSubsystemToEnergyConsumerMap = null;
- return;
+ return null;
}
final EnergyConsumer[] energyConsumers = mPowerStatsInternal.getEnergyConsumerInfo();
- if (energyConsumers == null) {
- // EnergyConsumer data unavailable, don't bother populating maps.
- mEnergyConsumerToSubsystemMap = null;
- mSubsystemToEnergyConsumerMap = null;
- return;
- }
-
- final int length = energyConsumers.length;
- if (length == 0) {
- // EnergyConsumer array empty, don't bother populating maps.
- mEnergyConsumerToSubsystemMap = null;
- mSubsystemToEnergyConsumerMap = null;
- return;
- } else {
- mEnergyConsumerToSubsystemMap = new SparseIntArray(length);
- mSubsystemToEnergyConsumerMap = new SparseIntArray(length);
+ if (energyConsumers == null || energyConsumers.length == 0) {
+ return null;
}
+ // TODO: Initialize typeToIds
+ // Maps type -> {ids} (1:n map, since multiple ids might have the same type)
+ // final SparseArray<SparseIntArray> typeToIds = new SparseArray<>();
+
+ // Maps id -> EnergyConsumer (1:1 map)
+ final SparseArray<EnergyConsumer> idToConsumer = new SparseArray<>(energyConsumers.length);
+
// Add all expected EnergyConsumers to the maps
- for (int i = 0; i < length; i++) {
- final EnergyConsumer consumer = energyConsumers[i];
- switch (consumer.type) {
- case EnergyConsumerType.DISPLAY:
- if (consumer.ordinal == 0) {
- mEnergyConsumerToSubsystemMap.put(consumer.id,
- MeasuredEnergyArray.SUBSYSTEM_DISPLAY);
- mSubsystemToEnergyConsumerMap.put(MeasuredEnergyArray.SUBSYSTEM_DISPLAY,
- consumer.id);
- } else {
- Slog.w(TAG, "Unexpected ordinal (" + consumer.ordinal
- + ") for EnergyConsumerType.DISPLAY");
- }
- break;
- default:
- Slog.w(TAG, "Unexpected EnergyConsumerType (" + consumer.type + ")");
+ for (final EnergyConsumer consumer : energyConsumers) {
+ // Check for inappropriate ordinals
+ if (consumer.ordinal != 0) {
+ switch (consumer.type) {
+ case EnergyConsumerType.OTHER:
+ case EnergyConsumerType.CPU_CLUSTER:
+ break;
+ default:
+ Slog.w(TAG, "EnergyConsumer '" + consumer.name + "' has unexpected ordinal "
+ + consumer.ordinal + " for type " + consumer.type);
+ continue; // Ignore this consumer
+ }
}
-
+ idToConsumer.put(consumer.id, consumer);
+ // TODO: Also populate typeToIds map
}
+ // TODO: Store typeToIds in mEnergyConsumerTypeToIdMap.
+ return idToConsumer;
}
}
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index b915c0ce44f3..9e0aa32a0b9e 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -17,132 +17,260 @@
package com.android.server.am;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.power.MeasuredEnergyArray;
-import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem;
import java.io.PrintWriter;
-import java.util.Arrays;
/**
- * Keeps snapshots of data from previously pulled MeasuredEnergyArrays.
+ * Keeps snapshots of data from previously pulled EnergyConsumerResults.
*/
@VisibleForTesting
public class MeasuredEnergySnapshot {
private static final String TAG = "MeasuredEnergySnapshot";
- private static final long UNAVAILABLE = -1;
+ public static final long UNAVAILABLE = -1L;
+
+ /** Map of {@link EnergyConsumer#id} to its corresponding {@link EnergyConsumer}. */
+ private final SparseArray<EnergyConsumer> mEnergyConsumers;
+
+ /** Number of ordinals for {@link EnergyConsumerType#OTHER}. */
+ private final int mNumOtherOrdinals;
/**
- * Energy snapshots from the last time each {@link MeasuredEnergySubsystem} was updated.
+ * Energy snapshots, mapping {@link EnergyConsumer#id} to energy (UJ) from the last time
+ * each {@link EnergyConsumer} was updated.
*
- * Note that the snapshots for different subsystems may have been taken at different times.
+ * Note that the snapshots for different ids may have been taken at different times.
+ * Note that energies for all existing ids are stored here, including each ordinal of type
+ * {@link EnergyConsumerType#OTHER} (tracking their total energy usage).
*
- * A snapshot is {@link #UNAVAILABLE} if the subsystem has never been updated (ie. unsupported).
+ * If an id is not present yet, it is treated as uninitialized (energy {@link #UNAVAILABLE}).
*/
- private final long[] mMeasuredEnergySnapshots;
+ private final SparseLongArray mMeasuredEnergySnapshots;
/**
- * Constructor that initializes to the given energyArray;
- * all subsystems not mentioned in initialEnergyArray are set to UNAVAILABLE.
+ * Energy snapshots <b>per uid</b> from the last time each {@link EnergyConsumer} of type
+ * {@link EnergyConsumerType#OTHER} was updated.
+ * It maps each OTHER {@link EnergyConsumer#id} to a SparseLongArray, which itself maps each
+ * uid to an energy (UJ). That is,
+ * mAttributionSnapshots.get(consumerId).get(uid) = energy used by uid for this consumer.
+ *
+ * If an id is not present yet, it is treated as uninitialized (i.e. each uid is unavailable).
+ * If an id is present but a uid is not present, that uid's energy is 0.
*/
- public MeasuredEnergySnapshot(MeasuredEnergyArray initialEnergyArray) {
- this(MeasuredEnergyArray.NUMBER_SUBSYSTEMS, initialEnergyArray);
- }
+ private final SparseArray<SparseLongArray> mAttributionSnapshots;
/**
- * Constructor (for testing) that initializes to the given energyArray and numSubsystems;
- * all subsystems not mentioned in initialEnergyArray are set to UNAVAILABLE.
+ * Constructor that initializes to the given id->EnergyConsumer map, indicating which consumers
+ * exist and what their details are.
*/
- @VisibleForTesting
- MeasuredEnergySnapshot(int numSubsystems, MeasuredEnergyArray initialEnergyArray) {
- if (initialEnergyArray.size() > numSubsystems) {
- throw new IllegalArgumentException("Energy array contains " + initialEnergyArray.size()
- + " subsystems, which exceeds the maximum allowed of " + numSubsystems);
- }
- mMeasuredEnergySnapshots = new long[numSubsystems];
- Arrays.fill(mMeasuredEnergySnapshots, UNAVAILABLE);
- fillGivenSubsystems(initialEnergyArray);
+ MeasuredEnergySnapshot(@NonNull SparseArray<EnergyConsumer> idToConsumerMap) {
+ mEnergyConsumers = idToConsumerMap;
+ mMeasuredEnergySnapshots = new SparseLongArray(mEnergyConsumers.size());
+
+ mNumOtherOrdinals = calculateNumOtherOrdinals(idToConsumerMap);
+ mAttributionSnapshots = new SparseArray<>(mNumOtherOrdinals);
}
/**
- * For the subsystems present in energyArray, overwrites mMeasuredEnergySnapshots with their
- * energy values from energyArray.
+ * Returns the number of ordinals for {@link EnergyConsumerType#OTHER}, i.e. the number of
+ * custom energy buckets supported by the device.
*/
- private void fillGivenSubsystems(MeasuredEnergyArray energyArray) {
- final int size = energyArray.size();
- for (int i = 0; i < size; i++) {
- final int subsystem = energyArray.getSubsystem(i);
- mMeasuredEnergySnapshots[subsystem] = energyArray.getEnergy(i);
- }
+ public int getNumOtherOrdinals() {
+ return mNumOtherOrdinals;
+ }
+
+ /** Class for returning measured energy delta data. */
+ static class MeasuredEnergyDeltaData {
+ /** The energyUJ for {@link EnergyConsumerType#DISPLAY}. */
+ public long displayEnergyUJ = UNAVAILABLE;
+
+ /** Map of {@link EnergyConsumerType#OTHER} ordinals to their total energyUJ. */
+ public @Nullable long[] otherTotalEnergyUJ = null;
+
+ /** Map of {@link EnergyConsumerType#OTHER} ordinals to their {uid->energyUJ} maps. */
+ public @Nullable SparseLongArray[] otherUidEnergiesUJ = null;
}
/**
* Update with the some freshly measured energies and return the difference (delta)
* between the previously stored values and the passed-in values.
*
- * @param energyArray measured energy array for some (possibly not all) subsystems.
+ * @param ecrs EnergyConsumerResults for some (possibly not all) {@link EnergyConsumer}s.
+ * Consumers that are not present are ignored (they are *not* treated as 0).
*
- * @return a map from the updated subsystems to their corresponding energy deltas.
- * Subsystems not present in energyArray will not appear.
- * Subsystems with no difference in energy will not appear.
- * Returns null, if energyArray is null.
+ * @return a MeasuredEnergyDeltaData, containing maps from the updated consumers to
+ * their corresponding energy deltas.
+ * Fields with no interesting data (consumers not present in ecrs or with no energy
+ * difference) will generally be left as their default values.
+ * otherTotalEnergyUJ and otherUidEnergiesUJ are always either both null or both of
+ * length {@link #getNumOtherOrdinals()}.
+ * Returns null, if ecrs is null or empty.
*/
- public @Nullable SparseLongArray updateAndGetDelta(MeasuredEnergyArray energyArray) {
- if (energyArray == null) {
+ public @Nullable MeasuredEnergyDeltaData updateAndGetDelta(EnergyConsumerResult[] ecrs) {
+ if (ecrs == null || ecrs.length == 0) {
return null;
}
- final SparseLongArray delta = new SparseLongArray();
- final int size = energyArray.size();
- for (int i = 0; i < size; i++) {
- final int updatedSubsystem = energyArray.getSubsystem(i);
- final long newEnergyUJ = energyArray.getEnergy(i);
- final long oldEnergyUJ = mMeasuredEnergySnapshots[updatedSubsystem];
-
- // If this is the first valid energy, there is no delta to take.
- if (oldEnergyUJ < 0) continue;
+ final MeasuredEnergyDeltaData output = new MeasuredEnergyDeltaData();
+
+ for (final EnergyConsumerResult ecr : ecrs) {
+ // Extract the new energy data for the current consumer.
+ final int consumerId = ecr.id;
+ final long newEnergyUJ = ecr.energyUWs;
+ final EnergyConsumerAttribution[] newAttributions = ecr.attribution;
+
+ // Look up the static information about this consumer.
+ final EnergyConsumer consumer = mEnergyConsumers.get(consumerId, null);
+ if (consumer == null) {
+ Slog.e(TAG, "updateAndGetDelta given invalid consumerId " + consumerId);
+ continue;
+ }
+ final int type = consumer.type;
+ final int ordinal = consumer.ordinal;
+
+ // Look up, and update, the old energy information about this consumer.
+ final long oldEnergyUJ = mMeasuredEnergySnapshots.get(consumerId, UNAVAILABLE);
+ mMeasuredEnergySnapshots.put(consumerId, newEnergyUJ);
+ final SparseLongArray otherUidEnergies
+ = updateAndGetDeltaForTypeOther(consumer, newAttributions);
+
+ // Everything is fully done being updated. We now calculate the delta for returning.
+
+ // NB: Since sum(attribution.energyUWs)<=energyUWs we assume that if deltaEnergy==0
+ // there's no attribution either. Technically that isn't enforced at the HAL, but we
+ // can't really trust data like that anyway.
+
+ if (oldEnergyUJ < 0) continue; // Generally happens only on initialization.
+ if (newEnergyUJ == oldEnergyUJ) continue;
final long deltaUJ = newEnergyUJ - oldEnergyUJ;
- if (deltaUJ == 0) continue;
if (deltaUJ < 0) {
- Slog.e(TAG, "For subsystem " + updatedSubsystem + ", new energy (" + newEnergyUJ
- + ") is less than old energy (" + oldEnergyUJ + "). Skipping. ");
+ Slog.e(TAG, "EnergyConsumer " + consumer.name + ": new energy (" + newEnergyUJ
+ + ") < old energy (" + oldEnergyUJ + "). Skipping. ");
continue;
}
- delta.put(updatedSubsystem, deltaUJ);
- }
- fillGivenSubsystems(energyArray);
+ switch (type) {
+ case EnergyConsumerType.DISPLAY:
+ output.displayEnergyUJ = deltaUJ;
+ break;
+ case EnergyConsumerType.OTHER:
+ if (output.otherTotalEnergyUJ == null) {
+ output.otherTotalEnergyUJ = new long[getNumOtherOrdinals()];
+ output.otherUidEnergiesUJ = new SparseLongArray[getNumOtherOrdinals()];
+ }
+ output.otherTotalEnergyUJ[ordinal] = deltaUJ;
+ output.otherUidEnergiesUJ[ordinal] = otherUidEnergies;
+ break;
+ default:
+ Slog.w(TAG, "Ignoring consumer " + consumer.name + " of unknown type " + type);
- return delta;
+ }
+ }
+ return output;
}
/**
- * Check if a subsystem's measured energy is available.
- * @param subsystem which subsystem.
- * @return true if subsystem is available.
+ * For a consumer of type {@link EnergyConsumerType#OTHER}, updates
+ * {@link #mAttributionSnapshots} with freshly measured energies (per uid) and returns the
+ * difference (delta) between the previously stored values and the passed-in values.
+ *
+ * @param consumerInfo a consumer of type {@link EnergyConsumerType#OTHER}.
+ * @param newAttributions Record of uids and their new energyUJ values.
+ * Any uid not present is treated as having energy 0.
+ * If null or empty, all uids are treated as having energy 0.
+ * @return A map (in the sense of {@link MeasuredEnergyDeltaData#otherUidEnergiesUJ} for this
+ * consumer) of uid -> energyDelta, with all uids that have a non-zero energyDelta.
+ * Returns null if no delta available to calculate.
*/
- public boolean hasSubsystem(@MeasuredEnergySubsystem int subsystem) {
- return mMeasuredEnergySnapshots[subsystem] != UNAVAILABLE;
+ private @Nullable SparseLongArray updateAndGetDeltaForTypeOther(
+ @NonNull EnergyConsumer consumerInfo,
+ @Nullable EnergyConsumerAttribution[] newAttributions) {
+
+ if (consumerInfo.type != EnergyConsumerType.OTHER) {
+ return null;
+ }
+ if (newAttributions == null) {
+ // Treat null as empty (i.e. all uids have 0 energy).
+ newAttributions = new EnergyConsumerAttribution[0];
+ }
+
+ // SparseLongArray mapping uid -> energyUJ (for this particular consumerId)
+ SparseLongArray uidOldEnergyMap = mAttributionSnapshots.get(consumerInfo.id, null);
+
+ // If uidOldEnergyMap wasn't present, each uid was UNAVAILABLE, so update data and return.
+ if (uidOldEnergyMap == null) {
+ uidOldEnergyMap = new SparseLongArray(newAttributions.length);
+ mAttributionSnapshots.put(consumerInfo.id, uidOldEnergyMap);
+ for (EnergyConsumerAttribution newAttribution : newAttributions) {
+ uidOldEnergyMap.put(newAttribution.uid, newAttribution.energyUWs);
+ }
+ return null;
+ }
+
+ // Map uid -> energyDelta. No initial capacity since many deltas might be 0.
+ final SparseLongArray uidEnergyDeltas = new SparseLongArray();
+
+ for (EnergyConsumerAttribution newAttribution : newAttributions) {
+ final int uid = newAttribution.uid;
+ final long newEnergyUJ = newAttribution.energyUWs;
+ // uidOldEnergyMap was present. So any particular uid that wasn't present, had 0 energy.
+ final long oldEnergyUJ = uidOldEnergyMap.get(uid, 0L);
+ uidOldEnergyMap.put(uid, newEnergyUJ);
+
+ // Everything is fully done being updated. We now calculate the delta for returning.
+ if (oldEnergyUJ < 0) continue;
+ if (newEnergyUJ == oldEnergyUJ) continue;
+ final long deltaUJ = newEnergyUJ - oldEnergyUJ;
+ if (deltaUJ < 0) {
+ Slog.e(TAG, "EnergyConsumer " + consumerInfo.name + ": new energy (" + newEnergyUJ
+ + ") but old energy (" + oldEnergyUJ + "). Skipping. ");
+ continue;
+ }
+ uidEnergyDeltas.put(uid, deltaUJ);
+ }
+ return uidEnergyDeltas;
}
/** Dump debug data. */
public void dump(PrintWriter pw) {
- pw.println("Measured energy snapshot (microjoules):");
- pw.print(" ");
- for (int i = 0; i < MeasuredEnergyArray.NUMBER_SUBSYSTEMS; i++) {
- final long energyUJ = mMeasuredEnergySnapshots[i];
- if (energyUJ == UNAVAILABLE) continue;
- pw.print(MeasuredEnergyArray.SUBSYSTEM_NAMES[i]);
- pw.print(" : ");
- pw.print(energyUJ);
- if (i != MeasuredEnergyArray.NUMBER_SUBSYSTEMS - 1) {
- pw.print(", ");
- }
+ pw.println("Measured energy snapshot");
+ pw.println("List of EnergyConsumers:");
+ for (int i = 0; i < mEnergyConsumers.size(); i++) {
+ final int id = mEnergyConsumers.keyAt(i);
+ final EnergyConsumer consumer = mEnergyConsumers.valueAt(i);
+ pw.println(String.format(" Consumer %d is {id=%d, ordinal=%d, type=%d, name=%s}", id,
+ consumer.id, consumer.ordinal, consumer.type, consumer.name));
+ }
+ pw.println("Map of consumerIds to energy (in microjoules):");
+ for (int i = 0; i < mMeasuredEnergySnapshots.size(); i++) {
+ final int id = mMeasuredEnergySnapshots.keyAt(i);
+ final long energyUJ = mMeasuredEnergySnapshots.valueAt(i);
+ pw.println(String.format(" Consumer %d has energy %d uJ}", id, energyUJ));
}
+ pw.println("List of the " + mNumOtherOrdinals + " OTHER EnergyConsumers:");
+ pw.println(" " + mAttributionSnapshots);
pw.println();
}
+
+ /** Determines the number of ordinals for {@link EnergyConsumerType#OTHER}. */
+ private static int calculateNumOtherOrdinals(SparseArray<EnergyConsumer> idToConsumer) {
+ if (idToConsumer == null) return 0;
+ int numOtherOrdinals = 0;
+ final int size = idToConsumer.size();
+ for (int idx = 0; idx < size; idx++) {
+ final EnergyConsumer consumer = idToConsumer.valueAt(idx);
+ if (consumer.type == EnergyConsumerType.OTHER) numOtherOrdinals++;
+ }
+ return numOtherOrdinals;
+ }
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 10fe1e1d0684..c92abd48fdc2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3131,11 +3131,6 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, uid, true);
- if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
- scheduleOpNotedIfNeededLocked(code, uid, packageName, flags,
- AppOpsManager.MODE_IGNORED);
- return AppOpsManager.MODE_IGNORED;
- }
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
if (attributedOp.isRunning()) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
@@ -3145,6 +3140,12 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
+ if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
+ attributedOp.rejected(uidState.state, flags);
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, flags,
+ AppOpsManager.MODE_IGNORED);
+ return AppOpsManager.MODE_IGNORED;
+ }
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index baeb3fdda67d..81e90df5802b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -496,6 +496,7 @@ public class Sensor {
}
void setTestHalEnabled(boolean enabled) {
+ Slog.w(mTag, "setTestHalEnabled: " + enabled);
mTestHalEnabled = enabled;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 1d57073d6516..a38da3ad70b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -23,86 +23,85 @@ import android.hardware.biometrics.face.ISessionCallback;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.common.NativeHandle;
import android.hardware.keymaster.HardwareAuthToken;
-import android.os.Binder;
-import android.os.IBinder;
+import android.util.Slog;
/**
* Test HAL that provides only no-ops.
*/
public class TestHal extends IFace.Stub {
+ private static final String TAG = "face.aidl.TestHal";
@Override
public SensorProps[] getSensorProps() {
+ Slog.w(TAG, "getSensorProps");
return new SensorProps[0];
}
@Override
public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
- return new ISession() {
+ return new ISession.Stub() {
@Override
public void generateChallenge(int cookie, int timeoutSec) {
-
+ Slog.w(TAG, "generateChallenge, cookie: " + cookie);
}
@Override
public void revokeChallenge(int cookie, long challenge) {
-
+ Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
}
@Override
public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
byte enrollmentType, byte[] features, NativeHandle previewSurface) {
+ Slog.w(TAG, "enroll, cookie: " + cookie);
return null;
}
@Override
public ICancellationSignal authenticate(int cookie, long operationId) {
+ Slog.w(TAG, "authenticate, cookie: " + cookie);
return null;
}
@Override
public ICancellationSignal detectInteraction(int cookie) {
+ Slog.w(TAG, "detectInteraction, cookie: " + cookie);
return null;
}
@Override
public void enumerateEnrollments(int cookie) {
-
+ Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
}
@Override
public void removeEnrollments(int cookie, int[] enrollmentIds) {
-
+ Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
}
@Override
public void getFeatures(int cookie, int enrollmentId) {
-
+ Slog.w(TAG, "getFeatures, cookie: " + cookie);
}
@Override
public void setFeature(int cookie, HardwareAuthToken hat, int enrollmentId,
byte feature, boolean enabled) {
-
+ Slog.w(TAG, "setFeature, cookie: " + cookie);
}
@Override
public void getAuthenticatorId(int cookie) {
-
+ Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
}
@Override
public void invalidateAuthenticatorId(int cookie) {
-
+ Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
}
@Override
public void resetLockout(int cookie, HardwareAuthToken hat) {
-
- }
-
- @Override
- public IBinder asBinder() {
- return new Binder();
+ Slog.w(TAG, "resetLockout, cookie: " + cookie);
}
};
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
index bab1114dc70d..00ca8025564d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java
@@ -25,10 +25,12 @@ import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.biometrics.face.V1_1.IBiometricsFace;
import android.os.NativeHandle;
import android.os.RemoteException;
+import android.util.Slog;
import java.util.ArrayList;
public class TestHal extends IBiometricsFace.Stub {
+ private static final String TAG = "face.hidl.TestHal";
@Nullable
private IBiometricsFaceClientCallback mCallback;
@@ -47,6 +49,7 @@ public class TestHal extends IBiometricsFace.Stub {
@Override
public OptionalUint64 generateChallenge(int challengeTimeoutSec) {
+ Slog.w(TAG, "generateChallenge");
final OptionalUint64 result = new OptionalUint64();
result.status = Status.OK;
result.value = 0;
@@ -55,6 +58,7 @@ public class TestHal extends IBiometricsFace.Stub {
@Override
public int enroll(ArrayList<Byte> hat, int timeoutSec, ArrayList<Integer> disabledFeatures) {
+ Slog.w(TAG, "enroll");
return 0;
}
@@ -95,16 +99,19 @@ public class TestHal extends IBiometricsFace.Stub {
@Override
public int enumerate() {
+ Slog.w(TAG, "enumerate");
return 0;
}
@Override
public int remove(int faceId) {
+ Slog.w(TAG, "remove");
return 0;
}
@Override
public int authenticate(long operationId) {
+ Slog.w(TAG, "authenticate");
return 0;
}
@@ -115,6 +122,7 @@ public class TestHal extends IBiometricsFace.Stub {
@Override
public int resetLockout(ArrayList<Byte> hat) {
+ Slog.w(TAG, "resetLockout");
return 0;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 7e4ee9e77ab2..73b59cfdf248 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -476,6 +476,7 @@ class Sensor {
}
void setTestHalEnabled(boolean enabled) {
+ Slog.w(mTag, "setTestHalEnabled, enabled");
mTestHalEnabled = enabled;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index a31bcdcaf4e6..66b68eeb335b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -22,89 +22,89 @@ import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.ISessionCallback;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.keymaster.HardwareAuthToken;
-import android.os.Binder;
-import android.os.IBinder;
+import android.util.Slog;
/**
* Test HAL that provides only provides no-ops.
*/
public class TestHal extends IFingerprint.Stub {
+ private static final String TAG = "fingerprint.aidl.TestHal";
+
@Override
public SensorProps[] getSensorProps() {
+ Slog.w(TAG, "getSensorProps");
return new SensorProps[0];
}
@Override
public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
- return new ISession() {
+ return new ISession.Stub() {
@Override
public void generateChallenge(int cookie, int timeoutSec) {
-
+ Slog.w(TAG, "generateChallenge, cookie: " + cookie);
}
@Override
public void revokeChallenge(int cookie, long challenge) {
-
+ Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
}
@Override
public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) {
+ Slog.w(TAG, "enroll, cookie: " + cookie);
return null;
}
@Override
public ICancellationSignal authenticate(int cookie, long operationId) {
+ Slog.w(TAG, "authenticate, cookie: " + cookie);
return null;
}
@Override
public ICancellationSignal detectInteraction(int cookie) {
+ Slog.w(TAG, "detectInteraction, cookie: " + cookie);
return null;
}
@Override
public void enumerateEnrollments(int cookie) {
-
+ Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
}
@Override
public void removeEnrollments(int cookie, int[] enrollmentIds) {
-
+ Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
}
@Override
public void getAuthenticatorId(int cookie) {
-
+ Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
}
@Override
public void invalidateAuthenticatorId(int cookie) {
-
+ Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
}
@Override
public void resetLockout(int cookie, HardwareAuthToken hat) {
-
+ Slog.w(TAG, "resetLockout, cookie: " + cookie);
}
@Override
public void onPointerDown(int pointerId, int x, int y, float minor, float major) {
-
+ Slog.w(TAG, "onPointerDown");
}
@Override
public void onPointerUp(int pointerId) {
-
+ Slog.w(TAG, "onPointerUp");
}
@Override
public void onUiReady() {
-
- }
-
- @Override
- public IBinder asBinder() {
- return new Binder();
+ Slog.w(TAG, "onUiReady");
}
};
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
index b7aec0ed2731..57447f3a8cf7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
@@ -21,11 +21,14 @@ import android.hardware.biometrics.fingerprint.V2_1.FingerprintError;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
import android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint;
import android.os.RemoteException;
+import android.util.Slog;
/**
* Test HAL that provides only provides no-ops.
*/
public class TestHal extends IBiometricsFingerprint.Stub {
+ private static final String TAG = "fingerprint.hidl.TestHal";
+
@Nullable
private IBiometricsFingerprintClientCallback mCallback;
@@ -57,6 +60,7 @@ public class TestHal extends IBiometricsFingerprint.Stub {
@Override
public int enroll(byte[] hat, int gid, int timeoutSec) {
+ Slog.w(TAG, "enroll");
return 0;
}
@@ -80,11 +84,13 @@ public class TestHal extends IBiometricsFingerprint.Stub {
@Override
public int enumerate() {
+ Slog.w(TAG, "Enumerate");
return 0;
}
@Override
public int remove(int gid, int fid) {
+ Slog.w(TAG, "Remove");
return 0;
}
@@ -95,6 +101,7 @@ public class TestHal extends IBiometricsFingerprint.Stub {
@Override
public int authenticate(long operationId, int gid) {
+ Slog.w(TAG, "Authenticate");
return 0;
}
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index fa063b223250..225da7ad87b3 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -42,8 +42,8 @@ import android.util.MathUtils;
import android.util.Slog;
import android.util.TimeUtils;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 4832e46be8a8..a62f67a743ad 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -28,8 +28,8 @@ import android.util.Pair;
import android.util.Slog;
import android.util.Spline;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.Preconditions;
import com.android.server.display.utils.Plog;
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
new file mode 100644
index 000000000000..d4556ed5f9fa
--- /dev/null
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.DisplayAddress;
+
+import com.android.server.display.layout.Layout;
+
+import java.util.Arrays;
+
+/**
+ * Mapping from device states into {@link Layout}s. This allows us to map device
+ * states into specific layouts for the connected displays; particularly useful for
+ * foldable and multi-display devices where the default display and which displays are ON can
+ * change depending on the state of the device.
+ */
+class DeviceStateToLayoutMap {
+ private static final String TAG = "DeviceStateToLayoutMap";
+
+ public static final int STATE_DEFAULT = DeviceStateManager.INVALID_DEVICE_STATE;
+
+ // TODO - b/168208162 - Remove these when we check in static definitions for layouts
+ public static final int STATE_FOLDED = 100;
+ public static final int STATE_UNFOLDED = 101;
+
+ private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
+
+ DeviceStateToLayoutMap(Context context) {
+ mLayoutMap.append(STATE_DEFAULT, new Layout());
+ loadFoldedDisplayConfig(context);
+ }
+
+ public void dumpLocked(IndentingPrintWriter ipw) {
+ ipw.println("DeviceStateToLayoutMap:");
+ ipw.increaseIndent();
+
+ ipw.println("Registered Layouts:");
+ for (int i = 0; i < mLayoutMap.size(); i++) {
+ ipw.println("state(" + mLayoutMap.keyAt(i) + "): " + mLayoutMap.valueAt(i));
+ }
+ }
+
+ Layout get(int state) {
+ Layout layout = mLayoutMap.get(state);
+ if (layout == null) {
+ layout = mLayoutMap.get(STATE_DEFAULT);
+ }
+ return layout;
+ }
+
+ private Layout create(int state) {
+ if (mLayoutMap.contains(state)) {
+ Slog.e(TAG, "Attempted to create a second layout for state " + state);
+ return null;
+ }
+
+ final Layout layout = new Layout();
+ mLayoutMap.append(state, layout);
+ return layout;
+ }
+
+ private void loadFoldedDisplayConfig(Context context) {
+ final String[] strDisplayIds = context.getResources().getStringArray(
+ com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
+
+ if (strDisplayIds.length != 2 || TextUtils.isEmpty(strDisplayIds[0])
+ || TextUtils.isEmpty(strDisplayIds[1])) {
+ Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(strDisplayIds)
+ + "]");
+ return;
+ }
+
+ final long[] displayIds;
+ try {
+ displayIds = new long[] {
+ Long.parseLong(strDisplayIds[0]),
+ Long.parseLong(strDisplayIds[1])
+ };
+ } catch (NumberFormatException nfe) {
+ Slog.w(TAG, "Folded display config non numerical: " + Arrays.toString(strDisplayIds));
+ return;
+ }
+
+ final int[] foldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ // Only add folded states if folded state config is not empty
+ if (foldedDeviceStates.length == 0) {
+ return;
+ }
+
+ // Create the folded state layout
+ final Layout foldedLayout = create(STATE_FOLDED);
+ foldedLayout.createDisplayLocked(
+ DisplayAddress.fromPhysicalDisplayId(displayIds[0]), true /*isDefault*/);
+
+ // Create the unfolded state layout
+ final Layout unfoldedLayout = create(STATE_UNFOLDED);
+ unfoldedLayout.createDisplayLocked(
+ DisplayAddress.fromPhysicalDisplayId(displayIds[1]), true /*isDefault*/);
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index d6872217eab6..1b25427adf71 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -23,8 +23,8 @@ import android.os.PowerManager;
import android.util.Slog;
import android.view.DisplayAddress;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.R;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index bf16a6d5efb9..501533d535d3 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -26,7 +26,7 @@ import android.view.DisplayEventReceiver;
import android.view.RoundedCorners;
import android.view.Surface;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import java.util.Arrays;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 5c0fceb9b795..57f44864d2c0 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import android.annotation.NonNull;
import android.util.Slog;
import android.view.Display;
+import android.view.DisplayAddress;
import com.android.internal.annotations.GuardedBy;
import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -112,12 +113,11 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {
}
}
- public DisplayDevice getByIdLocked(@NonNull String uniqueId) {
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- final DisplayDevice d = mDisplayDevices.get(i);
- if (uniqueId.equals(d.getUniqueId())) {
- return d;
+ public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) {
+ for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
+ final DisplayDevice device = mDisplayDevices.get(i);
+ if (address.equals(device.getDisplayDeviceInfoLocked().address)) {
+ return device;
}
}
return null;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 01fee5645475..6a229417316d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -106,9 +106,9 @@ import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.AnimationThread;
@@ -1188,6 +1188,7 @@ public final class DisplayManagerService extends SystemService {
final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
final int state;
final int displayId = display.getDisplayIdLocked();
+
if (display.isEnabled()) {
state = mDisplayStates.get(displayId);
} else {
@@ -1564,12 +1565,6 @@ public final class DisplayManagerService extends SystemService {
}
}
- void setFoldOverride(Boolean isFolded) {
- synchronized (mSyncRoot) {
- mLogicalDisplayMapper.setFoldOverrideLocked(isFolded);
- }
- }
-
private void clearViewportsLocked() {
mViewports.clear();
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index aaea15a27e01..d1d04966bdec 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -60,8 +60,6 @@ class DisplayManagerShellCommand extends ShellCommand {
return setDisplayModeDirectorLoggingEnabled(false);
case "dwb-set-cct":
return setAmbientColorTemperatureOverride();
- case "set-fold":
- return setFoldOverride();
default:
return handleDefaultCommands(cmd);
}
@@ -92,8 +90,6 @@ class DisplayManagerShellCommand extends ShellCommand {
pw.println(" Disable display mode director logging.");
pw.println(" dwb-set-cct CCT");
pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable).");
- pw.println(" set-fold [fold|unfold|reset]");
- pw.println(" Simulates the 'fold' state of a device. 'reset' for default behavior.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -165,26 +161,4 @@ class DisplayManagerShellCommand extends ShellCommand {
mService.setAmbientColorTemperatureOverride(cct);
return 0;
}
-
- private int setFoldOverride() {
- String state = getNextArg();
- if (state == null) {
- getErrPrintWriter().println("Error: no parameter specified for set-fold");
- return 1;
- }
- final Boolean isFolded;
- if ("fold".equals(state)) {
- isFolded = true;
- } else if ("unfold".equals(state)) {
- isFolded = false;
- } else if ("reset".equals(state)) {
- isFolded = null;
- } else {
- getErrPrintWriter().println("Error: Invalid fold state request: " + state);
- return 1;
- }
-
- mService.setFoldOverride(isFolded);
- return 0;
- }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2df336528939..9320f5027ce0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -53,8 +53,8 @@ import android.util.Slog;
import android.util.TimeUtils;
import android.view.Display;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 173adce00cd9..1d20d878fb81 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -26,7 +26,7 @@ import android.util.Slog;
import android.view.Choreographer;
import android.view.Display;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 73ebb2e48037..3b66236c9f0f 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -38,7 +38,7 @@ import android.view.DisplayEventReceiver;
import android.view.RoundedCorners;
import android.view.SurfaceControl;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 80781d280d5e..20b133ce4d0a 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -32,6 +32,7 @@ import android.view.SurfaceControl;
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Arrays;
import java.util.Objects;
@@ -718,6 +719,7 @@ final class LogicalDisplay {
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mLayerStack=" + mLayerStack);
+ pw.println("mIsEnabled=" + mIsEnabled);
pw.println("mHasContent=" + mHasContent);
pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}");
pw.println("mRequestedColorMode=" + mRequestedColorMode);
@@ -731,4 +733,11 @@ final class LogicalDisplay {
pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
}
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ dumpLocked(new PrintWriter(sw));
+ return sw.toString();
+ }
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 16c4b2641433..a054db533e3f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -27,10 +27,10 @@ import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
+import com.android.server.display.layout.Layout;
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -75,40 +75,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private final boolean mSingleDisplayDemoMode;
/**
- * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
- * when {@link mIsFolded} is set to {@code true}.
- */
- private String mDisplayIdToUseWhenFolded;
-
- /**
- * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
- * when {@link mIsFolded} is set to {@code false}.
- */
- private String mDisplayIdToUseWhenUnfolded;
-
- /** Overrides the folded state of the device. For use with ADB commands. */
- private Boolean mIsFoldedOverride;
-
- /** Saves the last device fold state. */
- private boolean mIsFolded;
-
- /**
* List of all logical displays indexed by logical display id.
* Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
* TODO: multi-display - Move the aforementioned comment?
*/
private final SparseArray<LogicalDisplay> mLogicalDisplays =
new SparseArray<LogicalDisplay>();
- private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
- private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
/** A mapping from logical display id to display group. */
private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>();
private final DisplayDeviceRepository mDisplayDeviceRepo;
+ private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
private final Listener mListener;
private final int[] mFoldedDeviceStates;
+ private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ private Layout mCurrentLayout = null;
+ private boolean mIsFolded = false;
+
LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) {
mDisplayDeviceRepo = repo;
mListener = listener;
@@ -118,7 +103,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mFoldedDeviceStates = context.getResources().getIntArray(
com.android.internal.R.array.config_foldedDeviceStates);
- loadFoldedDisplayConfig(context);
+ mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(context);
}
@Override
@@ -213,13 +198,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
ipw.increaseIndent();
ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
- ipw.println("mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
+
+ ipw.println("mCurrentLayout=" + mCurrentLayout);
final int logicalDisplayCount = mLogicalDisplays.size();
ipw.println();
ipw.println("Logical Displays: size=" + logicalDisplayCount);
-
-
for (int i = 0; i < logicalDisplayCount; i++) {
int displayId = mLogicalDisplays.keyAt(i);
LogicalDisplay display = mLogicalDisplays.valueAt(i);
@@ -229,6 +213,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
ipw.decreaseIndent();
ipw.println();
}
+ mDeviceStateToLayoutMap.dumpLocked(ipw);
}
void setDeviceStateLocked(int state) {
@@ -244,79 +229,55 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
void setDeviceFoldedLocked(boolean isFolded) {
mIsFolded = isFolded;
- if (mIsFoldedOverride != null) {
- isFolded = mIsFoldedOverride.booleanValue();
+
+ // Until we have fully functioning state mapping, use hardcoded states based on isFolded
+ final int state = mIsFolded ? DeviceStateToLayoutMap.STATE_FOLDED
+ : DeviceStateToLayoutMap.STATE_UNFOLDED;
+
+ if (DEBUG) {
+ Slog.d(TAG, "New device state: " + state);
}
- if (mDisplayIdToUseWhenFolded == null || mDisplayIdToUseWhenUnfolded == null
- || mLogicalDisplays.size() < 2) {
- // Do nothing if this behavior is disabled or there are less than two displays.
+ final Layout layout = mDeviceStateToLayoutMap.get(state);
+ if (layout == null) {
return;
}
-
- final DisplayDevice deviceFolded =
- mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenFolded);
- final DisplayDevice deviceUnfolded =
- mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenUnfolded);
- if (deviceFolded == null || deviceUnfolded == null) {
- // If the expected devices for folding functionality are not present, return early.
+ final Layout.Display displayLayout = layout.getById(Display.DEFAULT_DISPLAY);
+ if (displayLayout == null) {
return;
}
-
- // Find the associated LogicalDisplays for the configured "folding" DeviceDisplays.
- final LogicalDisplay displayFolded = getLocked(deviceFolded);
- final LogicalDisplay displayUnfolded = getLocked(deviceUnfolded);
- if (displayFolded == null || displayUnfolded == null) {
- // If the expected displays are not present, return early.
+ final DisplayDevice newDefaultDevice =
+ mDisplayDeviceRepo.getByAddressLocked(displayLayout.getAddress());
+ if (newDefaultDevice == null) {
return;
}
- // Find out which display is currently default and which is disabled.
final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
- final LogicalDisplay disabledDisplay;
- if (defaultDisplay == displayFolded) {
- disabledDisplay = displayUnfolded;
- } else if (defaultDisplay == displayUnfolded) {
- disabledDisplay = displayFolded;
- } else {
- // If neither folded or unfolded displays are currently set to the default display, we
- // are in an unknown state and it's best to log the error and bail.
- Slog.e(TAG, "Unexpected: when attempting to swap displays, neither of the two"
- + " configured displays were set up as the default display. Default: "
- + defaultDisplay.getDisplayInfoLocked() + ", ConfiguredDisplays: [ folded="
- + displayFolded.getDisplayInfoLocked() + ", unfolded="
- + displayUnfolded.getDisplayInfoLocked() + " ]");
+ mCurrentLayout = layout;
+
+ // If we're already set up accurately, return early
+ if (defaultDisplay.getPrimaryDisplayDeviceLocked() == newDefaultDevice) {
return;
}
- if (isFolded == (defaultDisplay == displayFolded)) {
- // Nothing to do, already in the right state.
+ // We need to swap the default display's display-device with the one that is supposed
+ // to be the default in the new layout.
+ final LogicalDisplay displayToSwap = getLocked(newDefaultDevice);
+ if (displayToSwap == null) {
+ Slog.w(TAG, "Canceling display swap - unexpected empty second display for: "
+ + newDefaultDevice);
return;
}
-
- // Everything was checked and we need to swap, lets swap.
- displayFolded.swapDisplaysLocked(displayUnfolded);
+ defaultDisplay.swapDisplaysLocked(displayToSwap);
// We ensure that the non-default Display is always forced to be off. This was likely
// already done in a previous iteration, but we do it with each swap in case something in
// the underlying LogicalDisplays changed: like LogicalDisplay recreation, for example.
defaultDisplay.setEnabled(true);
- disabledDisplay.setEnabled(false);
+ displayToSwap.setEnabled(false);
// Update the world
updateLogicalDisplaysLocked();
-
- if (DEBUG) {
- Slog.d(TAG, "Folded displays: isFolded: " + isFolded + ", defaultDisplay? "
- + defaultDisplay.getDisplayInfoLocked());
- }
- }
-
- void setFoldOverrideLocked(Boolean isFolded) {
- if (!Objects.equals(isFolded, mIsFoldedOverride)) {
- mIsFoldedOverride = isFolded;
- setDeviceFoldedLocked(mIsFolded);
- }
}
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
@@ -333,7 +294,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return;
}
- final int displayId = assignDisplayIdLocked(isDefault);
+ final int displayId = Layout.assignDisplayIdLocked(isDefault);
final int layerStack = assignLayerStackLocked(displayId);
final DisplayGroup displayGroup;
@@ -356,8 +317,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return;
}
- mLogicalDisplays.put(displayId, display);
+ // For foldable devices, we start the internal non-default displays as disabled.
+ // TODO - b/168208162 - this will be removed when we recalculate the layout with each
+ // display-device addition.
+ if (mFoldedDeviceStates.length > 0 && deviceInfo.type == Display.TYPE_INTERNAL
+ && !isDefault) {
+ display.setEnabled(false);
+ }
+ mLogicalDisplays.put(displayId, display);
displayGroup.addDisplayLocked(display);
mDisplayIdToGroupMap.append(displayId, displayGroup);
@@ -375,6 +343,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "New Display added: " + display);
+ }
}
/**
@@ -466,10 +438,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
}
- private int assignDisplayIdLocked(boolean isDefault) {
- return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
- }
-
private int assignDisplayGroupIdLocked(boolean isDefault) {
return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++;
}
@@ -480,21 +448,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return displayId;
}
- private void loadFoldedDisplayConfig(Context context) {
- final String[] displayIds = context.getResources().getStringArray(
- com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
-
- if (displayIds.length != 2 || TextUtils.isEmpty(displayIds[0])
- || TextUtils.isEmpty(displayIds[1])) {
- Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(displayIds)
- + "]");
- return;
- }
-
- mDisplayIdToUseWhenFolded = displayIds[0];
- mDisplayIdToUseWhenUnfolded = displayIds[1];
- }
-
public interface Listener {
void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
void onDisplayGroupEventLocked(int groupId, int event);
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 7916d816dc9b..26004a8da1a1 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -20,7 +20,7 @@ import android.animation.ValueAnimator;
import android.util.FloatProperty;
import android.view.Choreographer;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
/**
* A custom animator that progressively updates a property value at
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
new file mode 100644
index 000000000000..18f39e6ade1d
--- /dev/null
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.layout;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+import android.view.DisplayAddress;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Holds a collection of {@link Display}s. A single instance of this class describes
+ * how to organize one or more DisplayDevices into LogicalDisplays for a particular device
+ * state. For example, there may be one instance of this class to describe display layout when
+ * a foldable device is folded, and a second instance for when the device is unfolded.
+ */
+public class Layout {
+ private static final String TAG = "Layout";
+ private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1;
+
+ private final List<Display> mDisplays = new ArrayList<>(2);
+
+ /**
+ * @return The default display ID, or a new unique one to use.
+ */
+ public static int assignDisplayIdLocked(boolean isDefault) {
+ return isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++;
+ }
+
+ @Override
+ public String toString() {
+ return mDisplays.toString();
+ }
+
+ /**
+ * Creates a simple 1:1 LogicalDisplay mapping for the specified DisplayDevice.
+ *
+ * @param address Address of the device.
+ * @param isDefault Indicates if the device is meant to be the default display.
+ * @return The new layout.
+ */
+ public Display createDisplayLocked(
+ @NonNull DisplayAddress address, boolean isDefault) {
+ if (contains(address)) {
+ Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
+ return null;
+ }
+
+ // See if we're dealing with the "default" display
+ if (isDefault && getById(DEFAULT_DISPLAY) != null) {
+ Slog.w(TAG, "Ignoring attempt to add a second default display: " + address);
+ isDefault = false;
+ }
+
+ // Assign a logical display ID and create the new display.
+ // Note that the logical display ID is saved into the layout, so when switching between
+ // different layouts, a logical display can be destroyed and later recreated with the
+ // same logical display ID.
+ final int logicalDisplayId = assignDisplayIdLocked(isDefault);
+ final Display layout = new Display(address, logicalDisplayId);
+
+ mDisplays.add(layout);
+ return layout;
+ }
+
+ /**
+ * @param address The address to check.
+ *
+ * @return True if the specified address is used in this layout.
+ */
+ public boolean contains(@NonNull DisplayAddress address) {
+ final int size = mDisplays.size();
+ for (int i = 0; i < size; i++) {
+ if (address.equals(mDisplays.get(i).getAddress())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param id The display ID to check.
+ *
+ * @return The display corresponding to the specified display ID.
+ */
+ public Display getById(int id) {
+ for (int i = 0; i < mDisplays.size(); i++) {
+ Display layout = mDisplays.get(i);
+ if (id == layout.getLogicalDisplayId()) {
+ return layout;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param index The index of the display to return.
+ *
+ * @return the display at the specified index.
+ */
+ public Display getAt(int index) {
+ return mDisplays.get(index);
+ }
+
+ /**
+ * @return The number of displays defined for this layout.
+ */
+ public int size() {
+ return mDisplays.size();
+ }
+
+ /**
+ * Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s.
+ */
+ public static class Display {
+ private final DisplayAddress mAddress;
+ private final int mLogicalDisplayId;
+
+ Display(@NonNull DisplayAddress address, int logicalDisplayId) {
+ mAddress = address;
+ mLogicalDisplayId = logicalDisplayId;
+ }
+
+ @Override
+ public String toString() {
+ return "{addr: " + mAddress + ", dispId: " + mLogicalDisplayId + "}";
+ }
+
+ public DisplayAddress getAddress() {
+ return mAddress;
+ }
+
+ public int getLogicalDisplayId() {
+ return mLogicalDisplayId;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index 909fcda26c39..66fc0d9c1760 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -17,6 +17,7 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.util.SparseIntArray;
@@ -108,7 +109,10 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) {
mPowerStatus.clear();
for (HdmiDeviceInfo info : deviceInfos) {
- mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
+ if (localDevice().mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0
+ || info.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+ mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
+ }
}
}
@@ -117,19 +121,22 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false);
resetPowerStatus(deviceInfos);
for (HdmiDeviceInfo info : deviceInfos) {
- final int logicalAddress = info.getLogicalAddress();
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
- logicalAddress),
- new SendMessageCallback() {
- @Override
- public void onSendCompleted(int error) {
- // If fails to send <Give Device Power Status>,
- // update power status into UNKNOWN.
- if (error != SendMessageResult.SUCCESS) {
- updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
+ if (localDevice().mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0
+ || info.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+ final int logicalAddress = info.getLogicalAddress();
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ logicalAddress),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ // If fails to send <Give Device Power Status>,
+ // update power status into UNKNOWN.
+ if (error != SendMessageResult.SUCCESS) {
+ updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
+ }
}
- }
- });
+ });
+ }
}
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 43c965dde27b..42b0add6136e 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -36,9 +36,9 @@ import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 402f6467e82d..af0aa769f8cc 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -217,7 +217,7 @@ public class BackgroundDexOptService extends JobService {
// trade-off worth doing to save boot time work.
int result = pm.performDexOptWithStatus(new DexoptOptions(
pkg,
- PackageManagerService.REASON_BOOT,
+ PackageManagerService.REASON_POST_BOOT,
DexoptOptions.DEXOPT_BOOT_COMPLETE));
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 52fdc7983636..308e815d7659 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -27,6 +27,8 @@ import android.content.pm.IDataLoaderManager;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -45,12 +47,20 @@ import java.util.List;
public class DataLoaderManagerService extends SystemService {
private static final String TAG = "DataLoaderManager";
private final Context mContext;
+ private final HandlerThread mThread;
+ private final Handler mHandler;
private final DataLoaderManagerBinderService mBinderService;
private SparseArray<DataLoaderServiceConnection> mServiceConnections = new SparseArray<>();
public DataLoaderManagerService(Context context) {
super(context);
mContext = context;
+
+ mThread = new HandlerThread(TAG);
+ mThread.start();
+
+ mHandler = new Handler(mThread.getLooper());
+
mBinderService = new DataLoaderManagerBinderService();
}
@@ -62,7 +72,7 @@ public class DataLoaderManagerService extends SystemService {
final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub {
@Override
public boolean bindToDataLoader(int dataLoaderId, DataLoaderParamsParcel params,
- IDataLoaderStatusListener listener) {
+ long bindDelayMs, IDataLoaderStatusListener listener) {
synchronized (mServiceConnections) {
if (mServiceConnections.get(dataLoaderId) != null) {
return true;
@@ -76,19 +86,21 @@ public class DataLoaderManagerService extends SystemService {
}
// Binds to the specific data loader service.
- DataLoaderServiceConnection connection = new DataLoaderServiceConnection(dataLoaderId,
- listener);
+ final DataLoaderServiceConnection connection = new DataLoaderServiceConnection(
+ dataLoaderId, listener);
- Intent intent = new Intent();
+ final Intent intent = new Intent();
intent.setComponent(dataLoaderComponent);
- if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
- UserHandle.of(UserHandle.getCallingUserId()))) {
- Slog.e(TAG,
- "Failed to bind to: " + dataLoaderComponent + " for ID=" + dataLoaderId);
- mContext.unbindService(connection);
- return false;
- }
- return true;
+
+ return mHandler.postDelayed(() -> {
+ if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
+ mHandler, UserHandle.of(UserHandle.getCallingUserId()))) {
+ Slog.e(TAG,
+ "Failed to bind to: " + dataLoaderComponent + " for ID="
+ + dataLoaderId);
+ mContext.unbindService(connection);
+ }
+ }, bindDelayMs);
}
/**
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index f9ae9b8f6879..e91bb46657e1 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -929,6 +929,7 @@ public class LauncherAppsService extends SystemService {
// Flag for bubble to make behaviour match documentLaunchMode=always.
intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ intents[0].putExtra(Intent.EXTRA_IS_BUBBLED, true);
}
intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index fc02b3439d16..b9e3e0f4450b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -687,7 +687,8 @@ public class PackageDexOptimizer {
boolean generateCompactDex = true;
switch (compilationReason) {
case PackageManagerService.REASON_FIRST_BOOT:
- case PackageManagerService.REASON_BOOT:
+ case PackageManagerService.REASON_BOOT_AFTER_OTA:
+ case PackageManagerService.REASON_POST_BOOT:
case PackageManagerService.REASON_INSTALL:
generateCompactDex = false;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7c425698e507..0ce26739b51c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3778,7 +3778,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), statusListener)) {
+ final long bindDelayMs = 0;
+ if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), bindDelayMs,
+ statusListener)) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to initialize data loader");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4db65501de06..7b9cf734fd30 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -780,17 +780,18 @@ public class PackageManagerService extends IPackageManager.Stub
// Compilation reasons.
public static final int REASON_UNKNOWN = -1;
public static final int REASON_FIRST_BOOT = 0;
- public static final int REASON_BOOT = 1;
- public static final int REASON_INSTALL = 2;
- public static final int REASON_INSTALL_FAST = 3;
- public static final int REASON_INSTALL_BULK = 4;
- public static final int REASON_INSTALL_BULK_SECONDARY = 5;
- public static final int REASON_INSTALL_BULK_DOWNGRADED = 6;
- public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 7;
- public static final int REASON_BACKGROUND_DEXOPT = 8;
- public static final int REASON_AB_OTA = 9;
- public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 10;
- public static final int REASON_SHARED = 11;
+ public static final int REASON_BOOT_AFTER_OTA = 1;
+ public static final int REASON_POST_BOOT = 2;
+ public static final int REASON_INSTALL = 3;
+ public static final int REASON_INSTALL_FAST = 4;
+ public static final int REASON_INSTALL_BULK = 5;
+ public static final int REASON_INSTALL_BULK_SECONDARY = 6;
+ public static final int REASON_INSTALL_BULK_DOWNGRADED = 7;
+ public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 8;
+ public static final int REASON_BACKGROUND_DEXOPT = 9;
+ public static final int REASON_AB_OTA = 10;
+ public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 11;
+ public static final int REASON_SHARED = 12;
public static final int REASON_LAST = REASON_SHARED;
@@ -11637,10 +11638,7 @@ public class PackageManagerService extends IPackageManager.Stub
// first boot, as they do not have profile data.
boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
- // We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
- boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
-
- if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
+ if (!causeUpgrade && !causeFirstBoot) {
return;
}
@@ -11657,7 +11655,7 @@ public class PackageManagerService extends IPackageManager.Stub
final long startTime = System.nanoTime();
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
- causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
+ causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA,
false /* bootComplete */);
final int elapsedTimeSeconds =
@@ -26213,30 +26211,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void setKeepUninstalledPackages(final List<String> packageList) {
- Preconditions.checkNotNull(packageList);
- List<String> removedFromList = null;
- synchronized (mLock) {
- if (mKeepUninstalledPackages != null) {
- final int packagesCount = mKeepUninstalledPackages.size();
- for (int i = 0; i < packagesCount; i++) {
- String oldPackage = mKeepUninstalledPackages.get(i);
- if (packageList != null && packageList.contains(oldPackage)) {
- continue;
- }
- if (removedFromList == null) {
- removedFromList = new ArrayList<>();
- }
- removedFromList.add(oldPackage);
- }
- }
- mKeepUninstalledPackages = new ArrayList<>(packageList);
- if (removedFromList != null) {
- final int removedCount = removedFromList.size();
- for (int i = 0; i < removedCount; i++) {
- deletePackageIfUnusedLPr(removedFromList.get(i));
- }
- }
- }
+ PackageManagerService.this.setKeepUninstalledPackagesInternal(packageList);
}
@Override
@@ -27734,6 +27709,43 @@ public class PackageManagerService extends IPackageManager.Stub
public DomainVerificationService.Connection getDomainVerificationConnection() {
return mDomainVerificationConnection;
}
+
+ @Override
+ public void setKeepUninstalledPackages(List<String> packageList) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.KEEP_UNINSTALLED_PACKAGES,
+ "setKeepUninstalledPackages requires KEEP_UNINSTALLED_PACKAGES permission");
+ Objects.requireNonNull(packageList);
+
+ setKeepUninstalledPackagesInternal(packageList);
+ }
+
+ private void setKeepUninstalledPackagesInternal(List<String> packageList) {
+ Preconditions.checkNotNull(packageList);
+ List<String> removedFromList = null;
+ synchronized (mLock) {
+ if (mKeepUninstalledPackages != null) {
+ final int packagesCount = mKeepUninstalledPackages.size();
+ for (int i = 0; i < packagesCount; i++) {
+ String oldPackage = mKeepUninstalledPackages.get(i);
+ if (packageList != null && packageList.contains(oldPackage)) {
+ continue;
+ }
+ if (removedFromList == null) {
+ removedFromList = new ArrayList<>();
+ }
+ removedFromList.add(oldPackage);
+ }
+ }
+ mKeepUninstalledPackages = new ArrayList<>(packageList);
+ if (removedFromList != null) {
+ final int removedCount = removedFromList.size();
+ for (int i = 0; i < removedCount; i++) {
+ deletePackageIfUnusedLPr(removedFromList.get(i));
+ }
+ }
+ }
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 9cd55a6bb07e..636db111be88 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -29,7 +29,8 @@ public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
public static final String REASON_STRINGS[] = {
"first-boot",
- "boot",
+ "boot-after-ota",
+ "post-boot",
"install",
"install-fast",
"install-bulk",
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 139654ef4561..3576950bd3a6 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -587,7 +587,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_ERROR = 0;
private static final int TRON_COMPILATION_REASON_UNKNOWN = 1;
private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2;
- private static final int TRON_COMPILATION_REASON_BOOT = 3;
+ private static final int TRON_COMPILATION_REASON_BOOT_DEPRECATED_SINCE_S = 3;
private static final int TRON_COMPILATION_REASON_INSTALL = 4;
private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5;
private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
@@ -605,6 +605,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED_WITH_DM = 18;
private static final int
TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED_WITH_DM = 19;
+ private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20;
+ private static final int TRON_COMPILATION_REASON_POST_BOOT = 21;
// The annotation to add as a suffix to the compilation reason when dexopt was
// performed with dex metadata.
@@ -618,7 +620,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN;
case "error" : return TRON_COMPILATION_REASON_ERROR;
case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT;
- case "boot" : return TRON_COMPILATION_REASON_BOOT;
+ case "boot-after-ota": return TRON_COMPILATION_REASON_BOOT_AFTER_OTA;
+ case "post-boot" : return TRON_COMPILATION_REASON_POST_BOOT;
case "install" : return TRON_COMPILATION_REASON_INSTALL;
case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT;
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 30c334d22b6a..32bee5809b11 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -38,6 +38,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.Objects;
+import java.util.Set;
/**
* Permission definition.
@@ -345,6 +346,14 @@ public final class Permission {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_ROLE) != 0;
}
+ public boolean isKnownSigner() {
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0;
+ }
+
+ public Set<String> getKnownCerts() {
+ return mPermissionInfo.knownCerts;
+ }
+
public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) {
if (!oldPackageName.equals(mPermissionInfo.packageName)) {
return;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index aff871118a34..0669581ad090 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3438,6 +3438,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Any pre-installed system app is allowed to get this permission.
allowed = true;
}
+ if (!allowed && bp.isKnownSigner()) {
+ // If the permission is to be granted to a known signer then check if any of this
+ // app's signing certificates are in the trusted certificate digest Set.
+ allowed = pkg.getSigningDetails().hasAncestorOrSelfWithDigest(bp.getKnownCerts());
+ }
// Deferred to be checked under permission data lock inside restorePermissionState().
//if (!allowed && bp.isDevelopment()) {
// // For development permissions, a development permission
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8e0d632dd1a8..88fdc4aad5cf 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -90,11 +90,11 @@ import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.KeyEvent;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 539b4138cc18..0d4360058788 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1612,9 +1612,6 @@ public class StatsPullAtomService extends SystemService {
// Aggregate times for the same uids.
SparseArray<long[]> aggregated = new SparseArray<>();
mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
- // For uids known to be aggregated from many entries allow mutating in place to avoid
- // many copies. Otherwise, copy before aggregating.
- boolean mutateInPlace = false;
if (UserHandle.isIsolated(uid)) {
// Skip individual isolated uids because they are recycled and quickly removed from
// the underlying data source.
@@ -1622,26 +1619,18 @@ public class StatsPullAtomService extends SystemService {
} else if (UserHandle.isSharedAppGid(uid)) {
// All shared app gids are accounted together.
uid = LAST_SHARED_APPLICATION_GID;
- mutateInPlace = true;
} else {
// Everything else is accounted under their base uid.
uid = UserHandle.getAppId(uid);
}
long[] aggCpuFreqTimeMs = aggregated.get(uid);
- if (aggCpuFreqTimeMs != null) {
- if (!mutateInPlace) {
- aggCpuFreqTimeMs = Arrays.copyOf(aggCpuFreqTimeMs, cpuFreqTimeMs.length);
- aggregated.put(uid, aggCpuFreqTimeMs);
- }
- for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
- aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex];
- }
- } else {
- if (mutateInPlace) {
- cpuFreqTimeMs = Arrays.copyOf(cpuFreqTimeMs, cpuFreqTimeMs.length);
- }
- aggregated.put(uid, cpuFreqTimeMs);
+ if (aggCpuFreqTimeMs == null) {
+ aggCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
+ aggregated.put(uid, aggCpuFreqTimeMs);
+ }
+ for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
+ aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex];
}
});
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index bbbd19f7a49e..b210339adf79 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -18,7 +18,7 @@ package com.android.server.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 6a4c276de2dd..792f372b0c49 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -18,7 +18,7 @@ package com.android.server.timedetector;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 7cd4184f2bc9..c4c620c41918 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -23,7 +23,7 @@ import static java.util.stream.Collectors.joining;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index bee66637fb2f..04dac7c2b198 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -92,13 +92,6 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
@GuardedBy("mLock")
private boolean mForceStop;
- // TODO(b/159207608): Remove this constructor once VibratorService is removed
- public VibrationThread(Vibration vib, VibratorController vibrator,
- PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
- VibrationCallbacks callbacks) {
- this(vib, toSparseArray(vibrator), wakeLock, batteryStatsService, callbacks);
- }
-
public VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators,
PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
VibrationCallbacks callbacks) {
@@ -286,12 +279,6 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
return filteredEffects;
}
- private static SparseArray<VibratorController> toSparseArray(VibratorController controller) {
- SparseArray<VibratorController> array = new SparseArray<>(1);
- array.put(controller.getVibratorInfo().getId(), controller);
- return array;
- }
-
/**
* Get the duration the vibrator will be on for given {@code waveform}, starting at {@code
* startIndex} until the next time it's vibrating amplitude is zero.
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 4e47984fa75c..a6029cd28029 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -73,10 +73,6 @@ static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) {
- // TODO(b/167946816): remove this once VibratorService is removed.
- if (vibratorId < 0) {
- return std::move(std::make_unique<vibrator::HalController>());
- }
vibrator::ManagerHalController* manager = android_server_VibratorManagerService_getManager();
if (manager == nullptr) {
return nullptr;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 56cb3d11d552..886c1e5b98ea 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -69,6 +69,14 @@ struct Constants {
static constexpr auto progressUpdateInterval = 1000ms;
static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2;
static constexpr auto minPerUidTimeout = progressUpdateInterval * 3;
+
+ // If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
+ static constexpr auto healthyDataLoaderUptime = 10min;
+ // 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs)
+ static constexpr auto minBindDelay = 10s;
+ static constexpr auto maxBindDelay = 10000s;
+ static constexpr auto bindDelayMultiplier = 10;
+ static constexpr auto bindDelayJitterDivider = 10;
};
static const Constants& constants() {
@@ -386,6 +394,28 @@ void IncrementalService::onDump(int fd) {
dprintf(fd, "}\n");
}
+bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) {
+ if (ifs.dataLoaderStub->params().packageName == Constants::systemPackage) {
+ return true;
+ }
+
+ // Check all permanent binds.
+ for (auto&& [_, bindPoint] : ifs.bindPoints) {
+ if (bindPoint.kind != BindKind::Permanent) {
+ continue;
+ }
+ const auto progress = getLoadingProgressFromPath(ifs, bindPoint.sourceDir,
+ /*stopOnFirstIncomplete=*/true);
+ if (!progress.isError() && !progress.fullyLoaded()) {
+ LOG(INFO) << "Non system mount: [" << bindPoint.sourceDir
+ << "], partial progress: " << progress.getProgress() * 100 << "%";
+ return true;
+ }
+ }
+
+ return false;
+}
+
void IncrementalService::onSystemReady() {
if (mSystemReady.exchange(true)) {
return;
@@ -396,8 +426,11 @@ void IncrementalService::onSystemReady() {
std::lock_guard l(mLock);
mounts.reserve(mMounts.size());
for (auto&& [id, ifs] : mMounts) {
- if (ifs->mountId == id &&
- ifs->dataLoaderStub->params().packageName == Constants::systemPackage) {
+ if (ifs->mountId != id) {
+ continue;
+ }
+
+ if (needStartDataLoaderLocked(*ifs)) {
mounts.push_back(ifs);
}
}
@@ -1539,6 +1572,11 @@ static long elapsedMcs(Duration start, Duration end) {
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}
+template <class Duration>
+static constexpr auto castToMs(Duration d) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(d);
+}
+
// Extract lib files from zip, create new files in incfs and write data to them
// Lib files should be placed next to the APK file in the following matter:
// Example:
@@ -2134,9 +2172,43 @@ void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
<< status << " (current " << mCurrentStatus << ")";
}
+Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
+ std::unique_lock lock(mMutex);
+ const auto previousBindTs = mPreviousBindTs;
+ const auto now = Clock::now();
+ mPreviousBindTs = now;
+
+ const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms);
+ if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
+ nonCrashingInterval > Constants::healthyDataLoaderUptime) {
+ mPreviousBindDelay = 0ms;
+ return mPreviousBindDelay;
+ }
+
+ constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
+ constexpr auto maxBindDelayMs = castToMs(Constants::maxBindDelay);
+
+ const auto bindDelayMs =
+ std::min(std::max(mPreviousBindDelay * Constants::bindDelayMultiplier, minBindDelayMs),
+ maxBindDelayMs)
+ .count();
+ const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
+ const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs;
+ mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);
+
+ return mPreviousBindDelay;
+}
+
bool IncrementalService::DataLoaderStub::bind() {
+ const auto bindDelay = updateBindDelay();
+ if (bindDelay > 1s) {
+ LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
+ << bindDelay.count() / 1000 << "s";
+ }
+
bool result = false;
- auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, this, &result);
+ auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(),
+ this, &result);
if (!status.isOk() || !result) {
LOG(ERROR) << "Failed to bind a data loader for mount " << id();
return false;
@@ -2249,7 +2321,8 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
listener = mStatusListener;
- if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE) {
+ if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
+ mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
// For unavailable, unbind from DataLoader to ensure proper re-commit.
setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
@@ -2544,6 +2617,9 @@ void IncrementalService::DataLoaderStub::onDump(int fd) {
dprintf(fd, " blockIndex: %d\n", pendingRead.block);
dprintf(fd, " bootClockTsUs: %lld\n", (long long)pendingRead.bootClockTsUs);
}
+ dprintf(fd, " bind: %llds ago (delay: %llds)\n",
+ (long long)(elapsedMcs(mPreviousBindTs, Clock::now()) / 1000000),
+ (long long)(mPreviousBindDelay.count() / 1000));
dprintf(fd, " }\n");
const auto& params = mParams;
dprintf(fd, " dataLoaderParams: {\n");
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 5d53bac777b5..459ed3293510 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -245,7 +245,6 @@ private:
void setTargetStatusLocked(int status);
bool fsmStep();
- bool fsmStep(int currentStatus, int targetStatus);
void onHealthStatus(StorageHealthListener healthListener, int healthStatus);
void updateHealthStatus(bool baseline = false);
@@ -259,6 +258,8 @@ private:
BootClockTsUs getOldestPendingReadTs();
+ Milliseconds updateBindDelay();
+
void registerForPendingReads();
void unregisterFromPendingReads();
@@ -276,6 +277,9 @@ private:
int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
TimePoint mTargetStatusTs = {};
+ TimePoint mPreviousBindTs = {};
+ Milliseconds mPreviousBindDelay = {};
+
std::string mHealthPath;
incfs::UniqueControl mHealthControl;
struct {
@@ -370,6 +374,8 @@ private:
void addBindMountRecordLocked(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
std::string&& source, std::string&& target, BindKind kind);
+ bool needStartDataLoaderLocked(IncFsMount& ifs);
+
DataLoaderStubPtr prepareDataLoader(IncFsMount& ifs,
content::pm::DataLoaderParamsParcel&& params,
const DataLoaderStatusListener* statusListener = nullptr,
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 25d3f77fa58b..659d6503e11c 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -70,9 +70,10 @@ public:
~RealDataLoaderManager() = default;
binder::Status bindToDataLoader(MountId mountId,
const content::pm::DataLoaderParamsParcel& params,
+ int bindDelayMs,
const sp<content::pm::IDataLoaderStatusListener>& listener,
bool* _aidl_return) const final {
- return mInterface->bindToDataLoader(mountId, params, listener, _aidl_return);
+ return mInterface->bindToDataLoader(mountId, params, bindDelayMs, listener, _aidl_return);
}
binder::Status getDataLoader(MountId mountId,
sp<content::pm::IDataLoader>* _aidl_return) const final {
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 71fd3acc6489..d60035a9274d 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -63,7 +63,7 @@ class DataLoaderManagerWrapper {
public:
virtual ~DataLoaderManagerWrapper() = default;
virtual binder::Status bindToDataLoader(
- MountId mountId, const content::pm::DataLoaderParamsParcel& params,
+ MountId mountId, const content::pm::DataLoaderParamsParcel& params, int bindDelayMs,
const sp<content::pm::IDataLoaderStatusListener>& listener, bool* result) const = 0;
virtual binder::Status getDataLoader(MountId mountId,
sp<content::pm::IDataLoader>* result) const = 0;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 8713f9d3d821..ab491efe8583 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -208,8 +208,9 @@ public:
EXPECT_TRUE(mDataLoaderHolder != nullptr);
}
- MOCK_CONST_METHOD4(bindToDataLoader,
+ MOCK_CONST_METHOD5(bindToDataLoader,
binder::Status(int32_t mountId, const DataLoaderParamsParcel& params,
+ int bindDelayMs,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return));
MOCK_CONST_METHOD2(getDataLoader,
@@ -217,11 +218,11 @@ public:
MOCK_CONST_METHOD1(unbindFromDataLoader, binder::Status(int32_t mountId));
void bindToDataLoaderSuccess() {
- ON_CALL(*this, bindToDataLoader(_, _, _, _))
+ ON_CALL(*this, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(this, &MockDataLoaderManager::bindToDataLoaderOk));
}
void bindToDataLoaderFails() {
- ON_CALL(*this, bindToDataLoader(_, _, _, _))
+ ON_CALL(*this, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Return(
(binder::Status::fromExceptionCode(1, String8("failed to prepare")))));
}
@@ -234,6 +235,7 @@ public:
.WillByDefault(Invoke(this, &MockDataLoaderManager::unbindFromDataLoaderOk));
}
binder::Status bindToDataLoaderOk(int32_t mountId, const DataLoaderParamsParcel& params,
+ int bindDelayMs,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return) {
mId = mountId;
@@ -245,6 +247,40 @@ public:
}
return binder::Status::ok();
}
+ binder::Status bindToDataLoaderOkWith10sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 <= bindDelayMs && bindDelayMs <= 1000 * 11) << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+ binder::Status bindToDataLoaderOkWith100sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 * 9 < bindDelayMs && bindDelayMs < 1000 * 11 * 11) << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+ binder::Status bindToDataLoaderOkWith1000sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 * 9 * 9 < bindDelayMs && bindDelayMs < 1000 * 11 * 11 * 11) << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+ binder::Status bindToDataLoaderOkWith10000sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 * 9 * 9 * 9 < bindDelayMs && bindDelayMs < 1000 * 11 * 11 * 11 * 11)
+ << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+
binder::Status getDataLoaderOk(int32_t mountId, sp<IDataLoader>* _aidl_return) {
*_aidl_return = mDataLoader;
return binder::Status::ok();
@@ -261,6 +297,9 @@ public:
void setDataLoaderStatusUnavailable() {
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE);
}
+ void setDataLoaderStatusUnrecoverable() {
+ mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE);
+ }
binder::Status unbindFromDataLoaderOk(int32_t id) {
if (mDataLoader) {
if (auto status = mDataLoader->destroy(id); !status.isOk()) {
@@ -676,7 +715,7 @@ protected:
TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
mVold->mountIncFsFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
@@ -686,7 +725,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel) {
mVold->mountIncFsInvalidControlParcel();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
TemporaryDir tempDir;
int storageId =
@@ -698,7 +737,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel)
TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) {
mVold->mountIncFsSuccess();
mIncFs->makeFileFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
@@ -712,7 +751,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
@@ -727,7 +766,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) {
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mDataLoaderManager->bindToDataLoaderFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
@@ -755,11 +794,11 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
mIncrementalService->deleteStorage(storageId);
}
-TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
+TEST_F(IncrementalServiceTest, testDataLoaderDestroyedAndDelayed) {
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(6);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
- EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
- EXPECT_CALL(*mDataLoader, start(_)).Times(2);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(6);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(6);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
@@ -768,13 +807,38 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
+
// Simulated crash/other connection breakage.
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith100sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
mDataLoaderManager->setDataLoaderStatusDestroyed();
}
TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
mDataLoader->initializeCreateOkNoStatus();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
@@ -793,7 +857,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
mDataLoader->initializeCreateOkNoStatus();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
@@ -811,7 +875,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
mDataLoader->initializeCreateOkNoStatus();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
@@ -827,12 +891,30 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
mDataLoaderManager->setDataLoaderStatusUnavailable();
}
+TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnrecoverable) {
+ mDataLoader->initializeCreateOkNoStatus();
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+ mDataLoaderManager->setDataLoaderStatusUnrecoverable();
+}
+
TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
mIncFs->waitForPendingReadsSuccess();
mIncFs->openMountSuccess();
mDataLoader->initializeCreateOkNoStatus();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(2);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
@@ -856,7 +938,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
mIncFs->openMountSuccess();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
@@ -1406,7 +1488,7 @@ static ErrorCode checkPerUidTimeoutsEmpty(
}
TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) {
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 97e75828ed91..a0e5c5deef61 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1291,7 +1291,6 @@ public final class SystemServer implements Dumpable {
t.traceBegin("startOtherServices");
final Context context = mSystemContext;
- VibratorService vibrator = null;
DynamicSystemService dynamicSystem = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
@@ -1417,11 +1416,6 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
t.traceEnd();
- t.traceBegin("StartVibratorService");
- vibrator = new VibratorService(context);
- ServiceManager.addService("vibrator", vibrator);
- t.traceEnd();
-
t.traceBegin("StartDynamicSystemService");
dynamicSystem = new DynamicSystemService(context);
ServiceManager.addService("dynamic_system", dynamicSystem);
@@ -2490,14 +2484,6 @@ public final class SystemServer implements Dumpable {
// It is now time to start up the app processes...
- t.traceBegin("MakeVibratorServiceReady");
- try {
- vibrator.systemReady();
- } catch (Throwable e) {
- reportWtf("making Vibrator Service ready", e);
- }
- t.traceEnd();
-
t.traceBegin("MakeLockSettingsServiceReady");
if (lockSettings != null) {
try {
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index cbebe6984794..2219d477630e 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -38,7 +38,6 @@ import static org.testng.Assert.expectThrows;
import android.annotation.UserIdInt;
import android.app.Application;
-import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
@@ -874,8 +873,7 @@ public class BackupManagerServiceRoboTest {
SecurityException.class,
() ->
backupManagerService.requestBackup(
- mUserTwoId, packages, observer, monitor, 0,
- OperationType.BACKUP));
+ mUserTwoId, packages, observer, monitor, 0));
}
/**
@@ -893,11 +891,9 @@ public class BackupManagerServiceRoboTest {
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
- backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
- OperationType.BACKUP);
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
- verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0,
- OperationType.BACKUP);
+ verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@@ -910,11 +906,9 @@ public class BackupManagerServiceRoboTest {
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0,
- OperationType.BACKUP);
+ backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
- verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0,
- OperationType.BACKUP);
+ verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
@@ -927,11 +921,9 @@ public class BackupManagerServiceRoboTest {
IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
- OperationType.BACKUP);
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
- verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0,
- OperationType.BACKUP);
+ verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
}
/**
@@ -1092,11 +1084,9 @@ public class BackupManagerServiceRoboTest {
registerUser(backupManagerService, mUserOneId, mUserOneService);
setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT,
- OperationType.BACKUP);
+ backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
- verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
- OperationType.BACKUP);
+ verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
}
/** Test that the backup service does not route methods for non-registered users. */
@@ -1106,11 +1096,9 @@ public class BackupManagerServiceRoboTest {
registerUser(backupManagerService, mUserOneId, mUserOneService);
setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT,
- OperationType.BACKUP);
+ backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
- verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
- OperationType.BACKUP);
+ verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
}
/** Test that the backup service routes methods correctly to the user that requests it. */
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
index f0d7006633a2..da3d1d6187fc 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -198,6 +198,10 @@ public class VibratorManagerServiceTest {
return mVibratorProviders.get(vibratorId)
.newVibratorController(vibratorId, listener);
}
+
+ @Override
+ void addService(String name, IBinder service) {
+ }
});
service.systemReady();
return service;
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
deleted file mode 100644
index 633957a8b13a..000000000000
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ /dev/null
@@ -1,757 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AppOpsManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManagerInternal;
-import android.hardware.input.IInputManager;
-import android.hardware.input.InputManager;
-import android.hardware.vibrator.IVibrator;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IVibratorStateListener;
-import android.os.Looper;
-import android.os.PowerManagerInternal;
-import android.os.PowerSaveState;
-import android.os.Process;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.os.VibratorInfo;
-import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.view.InputDevice;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.internal.util.test.FakeSettingsProviderRule;
-import com.android.server.vibrator.FakeVibrator;
-import com.android.server.vibrator.FakeVibratorControllerProvider;
-import com.android.server.vibrator.VibratorController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-/**
- * Tests for {@link VibratorService}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:VibratorServiceTest
- */
-@Presubmit
-public class VibratorServiceTest {
-
- private static final int TEST_TIMEOUT_MILLIS = 1_000;
- private static final int UID = Process.ROOT_UID;
- private static final int VIBRATOR_ID = 1;
- private static final String PACKAGE_NAME = "package";
- private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
- private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
- .setBatterySaverEnabled(true).build();
- private static final VibrationAttributes ALARM_ATTRS =
- new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
- private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_TOUCH).build();
- private static final VibrationAttributes NOTIFICATION_ATTRS =
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_NOTIFICATION).build();
- private static final VibrationAttributes RINGTONE_ATTRS =
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_RINGTONE).build();
-
- @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
- @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
-
- @Mock private PackageManagerInternal mPackageManagerInternalMock;
- @Mock private PowerManagerInternal mPowerManagerInternalMock;
- @Mock private AppOpsManager mAppOpsManagerMock;
- @Mock private IVibratorStateListener mVibratorStateListenerMock;
- @Mock private IInputManager mIInputManagerMock;
- @Mock private IBinder mVibratorStateListenerBinderMock;
-
- private TestLooper mTestLooper;
- private ContextWrapper mContextSpy;
- private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
- private FakeVibrator mFakeVibrator;
- private FakeVibratorControllerProvider mVibratorProvider;
-
- @Before
- public void setUp() throws Exception {
- mTestLooper = new TestLooper();
- mFakeVibrator = new FakeVibrator();
- mVibratorProvider = new FakeVibratorControllerProvider(mTestLooper.getLooper());
- mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
- InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
-
- ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
- when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
- when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
- when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
- when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
- when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock);
- when(mPackageManagerInternalMock.getSystemUiServiceComponent())
- .thenReturn(new ComponentName("", ""));
- doAnswer(invocation -> {
- mRegisteredPowerModeListener = invocation.getArgument(0);
- return null;
- }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
-
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
-
- addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
- addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
- }
-
- @After
- public void tearDown() throws Exception {
- InputManager.clearInstance();
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.removeServiceForTest(PowerManagerInternal.class);
- }
-
- private VibratorService createService() {
- VibratorService service = new VibratorService(mContextSpy,
- new VibratorService.Injector() {
- @Override
- VibratorController createVibratorController(
- VibratorController.OnVibrationCompleteListener listener) {
- return mVibratorProvider.newVibratorController(VIBRATOR_ID, listener);
- }
-
- @Override
- Handler createHandler(Looper looper) {
- return new Handler(mTestLooper.getLooper());
- }
-
- @Override
- void addService(String name, IBinder service) {
- // ignore
- }
- });
- service.systemReady();
- return service;
- }
-
- @Test
- public void createService_initializesNativeService() {
- createService();
- assertTrue(mVibratorProvider.isInitialized());
- }
-
- @Test
- public void hasVibrator_withVibratorHalPresent_returnsTrue() {
- assertTrue(createService().hasVibrator());
- }
-
- @Test
- public void hasVibrator_withNoVibratorHalPresent_returnsFalse() {
- mVibratorProvider.disableVibrators();
- assertFalse(createService().hasVibrator());
- }
-
- @Test
- public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() {
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- assertTrue(createService().hasAmplitudeControl());
- }
-
- @Test
- public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() {
- assertFalse(createService().hasAmplitudeControl());
- }
-
- @Test
- public void hasAmplitudeControl_withInputDevices_returnsTrue() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- assertTrue(createService().hasAmplitudeControl());
- }
-
- @Test
- public void getVibratorInfo_returnsSameInfoFromNative() {
- mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
- IVibrator.CAP_AMPLITUDE_CONTROL);
- mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- mVibratorProvider.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
-
- VibratorInfo info = createService().getVibratorInfo();
- assertTrue(info.hasAmplitudeControl());
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
- info.isEffectSupported(VibrationEffect.EFFECT_TICK));
- assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
- assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
- }
-
- @Test
- public void vibrate_withRingtone_usesRingtoneSettings() throws Exception {
- setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
- vibrate(createService(), VibrationEffect.createOneShot(1, 1), RINGTONE_ATTRS);
-
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
- vibrateAndWait(createService(), VibrationEffect.createOneShot(10, 10), RINGTONE_ATTRS);
-
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
- vibrateAndWait(createService(), VibrationEffect.createOneShot(100, 100), RINGTONE_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(2, effects.size());
- assertEquals(10, effects.get(0).getDuration());
- assertEquals(100, effects.get(1).getDuration());
- }
-
- @Test
- public void vibrate_withPowerModeChange_usesLowPowerModeState() throws Exception {
- VibratorService service = createService();
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS);
- vibrateAndWait(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS);
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- vibrateAndWait(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null);
- vibrateAndWait(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(3, effects.size());
- assertEquals(2, effects.get(0).getDuration());
- assertEquals(3, effects.get(1).getDuration());
- assertEquals(4, effects.get(2).getDuration());
- }
-
- @Test
- public void vibrate_withAudioAttributes_usesOriginalAudioUsageInAppOpsManager() {
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- AudioAttributes audioAttributes = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
- VibrationAttributes vibrationAttributes = new VibrationAttributes.Builder(
- audioAttributes, effect).build();
-
- vibrate(service, effect, vibrationAttributes);
-
- verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY), anyInt(), anyString());
- }
-
- @Test
- public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST).build());
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_UNKNOWN).build());
-
- InOrder inOrderVerifier = inOrder(mAppOpsManagerMock);
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_NOTIFICATION), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_NOTIFICATION_RINGTONE), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST),
- anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
- }
-
- @Test
- public void vibrate_withOneShotAndInputDevices_vibratesInputDevices() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createOneShot(100, 128);
- vibrate(service, effect, ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
- /* timeout= */ 20));
- }
-
- @Test
- public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude()
- throws Exception {
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- VibratorService service = createService();
-
- vibrateAndWait(service, VibrationEffect.createOneShot(100, 128), ALARM_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(1, effects.size());
- assertEquals(100, effects.get(0).getDuration());
- assertEquals(Arrays.asList(128), mVibratorProvider.getAmplitudes());
- }
-
- @Test
- public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude()
- throws Exception {
- VibratorService service = createService();
- clearInvocations();
-
- vibrateAndWait(service, VibrationEffect.createOneShot(100, 128), ALARM_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(1, effects.size());
- assertEquals(100, effects.get(0).getDuration());
- assertTrue(mVibratorProvider.getAmplitudes().isEmpty());
- }
-
- @Test
- public void vibrate_withPrebaked_performsEffect() throws Exception {
- mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
- vibrateAndWait(service, effect, ALARM_ATTRS);
-
- VibrationEffect.Prebaked expectedEffect = new VibrationEffect.Prebaked(
- VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
- assertEquals(Arrays.asList(expectedEffect), mVibratorProvider.getEffects());
- }
-
- @Test
- public void vibrate_withPrebakedAndInputDevices_vibratesFallbackWaveformOnInputDevices()
- throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), any(), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
- /* timeout= */ 20));
- }
-
- @Test
- public void vibrate_enteringLowPowerMode_cancelVibration() throws Exception {
- VibratorService service = createService();
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
- assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
- }
-
- @Test
- public void vibrate_enteringLowPowerModeAndRingtone_doNotCancelVibration() throws Exception {
- VibratorService service = createService();
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- vibrate(service, VibrationEffect.createOneShot(1000, 100), RINGTONE_ATTRS);
- assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- // Settings callback is async, so wait before checking it never got cancelled.
- assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
- }
-
- @Test
- public void vibrate_withSettingsChanged_doNotCancelVibration() throws Exception {
- VibratorService service = createService();
- vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
- assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
-
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
-
- // FakeSettingsProvider don't support testing triggering ContentObserver yet.
- service.updateVibrators();
-
- // Settings callback is async, so wait before checking it never got cancelled.
- assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
- }
-
- @Test
- public void vibrate_withComposed_performsEffect() throws Exception {
- mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
- .compose();
- vibrateAndWait(service, effect, ALARM_ATTRS);
- assertEquals(Arrays.asList(effect), mVibratorProvider.getEffects());
- }
-
- @Test
- public void vibrate_withComposedAndInputDevices_vibratesInputDevices() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- when(mIInputManagerMock.getInputDevice(2)).thenReturn(createInputDeviceWithVibrator(2));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
- .compose();
- vibrate(service, effect, ALARM_ATTRS);
- InOrder inOrderVerifier = inOrder(mIInputManagerMock);
- inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
- inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
- /* timeout= */ 20));
- }
-
- @Test
- public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime()
- throws Exception {
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createWaveform(
- new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
- vibrateAndWait(service, effect, ALARM_ATTRS);
-
- assertEquals(Arrays.asList(100, 200, 50), mVibratorProvider.getAmplitudes());
- assertEquals(
- Arrays.asList(VibrationEffect.createOneShot(30, VibrationEffect.DEFAULT_AMPLITUDE)),
- mVibratorProvider.getEffects());
- }
-
- @Test
- public void vibrate_withWaveformAndInputDevices_vibratesInputDevices() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createWaveform(
- new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
- vibrate(service, effect, ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
- /* timeout= */ 20));
- }
-
- @Test
- public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
- mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
-
- // Trigger callbacks from controller.
- mTestLooper.moveTimeForward(50);
- mTestLooper.dispatchAll();
- assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
- }
-
- @Test
- public void cancelVibrate_withDeviceVibrating_callsOff() throws Exception {
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
- assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
-
- service.cancelVibrate(service);
- assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
- }
-
- @Test
- public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
- VibratorService service = createService();
- service.registerVibratorStateListener(mVibratorStateListenerMock);
-
- vibrateAndWait(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
-
- InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
- // First notification done when listener is registered.
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true));
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
- inOrderVerifier.verifyNoMoreInteractions();
- }
-
- @Test
- public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
- VibratorService service = createService();
-
- service.registerVibratorStateListener(mVibratorStateListenerMock);
-
- vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS);
- assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
-
- service.unregisterVibratorStateListener(mVibratorStateListenerMock);
- // Trigger callbacks from controller.
- mTestLooper.moveTimeForward(50);
- mTestLooper.dispatchAll();
- assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
-
- InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
- // First notification done when listener is registered.
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true));
- inOrderVerifier.verify(mVibratorStateListenerMock, atLeastOnce()).asBinder(); // unregister
- inOrderVerifier.verifyNoMoreInteractions();
- }
-
- @Test
- public void scale_withPrebaked_userIntensitySettingAsEffectStrength() throws Exception {
- // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- mVibratorProvider.setSupportedEffects(
- VibrationEffect.EFFECT_CLICK,
- VibrationEffect.EFFECT_TICK,
- VibrationEffect.EFFECT_DOUBLE_CLICK,
- VibrationEffect.EFFECT_HEAVY_CLICK);
- VibratorService service = createService();
-
- vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
- NOTIFICATION_ATTRS);
- vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
- HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
-
- List<Integer> playedStrengths = mVibratorProvider.getEffects().stream()
- .map(VibrationEffect.Prebaked.class::cast)
- .map(VibrationEffect.Prebaked::getEffectStrength)
- .collect(Collectors.toList());
- assertEquals(Arrays.asList(
- VibrationEffect.EFFECT_STRENGTH_STRONG,
- VibrationEffect.EFFECT_STRENGTH_MEDIUM,
- VibrationEffect.EFFECT_STRENGTH_LIGHT),
- playedStrengths);
- }
-
- @Test
- public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception {
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
-
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- VibratorService service = createService();
-
- vibrateAndWait(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS);
- vibrateAndWait(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS);
- vibrateAndWait(service,
- VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1),
- HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS);
-
- List<Integer> amplitudes = mVibratorProvider.getAmplitudes();
- assertEquals(3, amplitudes.size());
- // Alarm vibration is never scaled.
- assertEquals(100, amplitudes.get(0).intValue());
- // Notification vibrations will be scaled with SCALE_VERY_HIGH.
- assertTrue(amplitudes.get(1) > 150);
- // Haptic feedback vibrations will be scaled with SCALE_LOW.
- assertTrue(amplitudes.get(2) < 100 && amplitudes.get(2) > 50);
- }
-
- @Test
- public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() throws Exception {
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
-
- mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
- .compose();
-
- vibrateAndWait(service, effect, ALARM_ATTRS);
- vibrateAndWait(service, effect, NOTIFICATION_ATTRS);
- vibrateAndWait(service, effect, HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, effect, RINGTONE_ATTRS);
-
- List<VibrationEffect.Composition.PrimitiveEffect> primitives =
- mVibratorProvider.getEffects().stream()
- .map(VibrationEffect.Composed.class::cast)
- .map(VibrationEffect.Composed::getPrimitiveEffects)
- .flatMap(List::stream)
- .collect(Collectors.toList());
-
- // Ringtone vibration is off, so only the other 3 are propagated to native.
- assertEquals(6, primitives.size());
-
- // Alarm vibration is never scaled.
- assertEquals(1f, primitives.get(0).scale, /* delta= */ 1e-2);
- assertEquals(0.5f, primitives.get(1).scale, /* delta= */ 1e-2);
-
- // Notification vibrations will be scaled with SCALE_VERY_HIGH.
- assertEquals(1f, primitives.get(2).scale, /* delta= */ 1e-2);
- assertTrue(0.7 < primitives.get(3).scale);
-
- // Haptic feedback vibrations will be scaled with SCALE_LOW.
- assertTrue(0.5 < primitives.get(4).scale);
- assertTrue(0.5 > primitives.get(5).scale);
- }
-
- private void vibrate(VibratorService service, VibrationEffect effect,
- VibrationAttributes attrs) {
- service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
- }
-
- private void vibrateAndWait(VibratorService service, VibrationEffect effect,
- VibrationAttributes attrs) throws Exception {
- CountDownLatch startedCount = new CountDownLatch(1);
- CountDownLatch finishedCount = new CountDownLatch(1);
- service.registerVibratorStateListener(new IVibratorStateListener() {
- @Override
- public void onVibrating(boolean vibrating) {
- if (vibrating) {
- startedCount.countDown();
- } else if (startedCount.getCount() == 0) {
- finishedCount.countDown();
- }
- }
-
- @Override
- public IBinder asBinder() {
- return mock(IBinder.class);
- }
- });
-
- mTestLooper.startAutoDispatch();
- service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
- assertTrue(startedCount.await(1, TimeUnit.SECONDS));
- assertTrue(finishedCount.await(1, TimeUnit.SECONDS));
- mTestLooper.stopAutoDispatchAndIgnoreExceptions();
- }
-
- private InputDevice createInputDeviceWithVibrator(int id) {
- return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, /* hasVibrator= */ true, false, false, false /* hasSensor */,
- false /* hasBattery */);
- }
-
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
- private void setRingerMode(int ringerMode) {
- AudioManager audioManager = mContextSpy.getSystemService(AudioManager.class);
- audioManager.setRingerModeInternal(ringerMode);
- assertEquals(ringerMode, audioManager.getRingerModeInternal());
- }
-
- private void setUserSetting(String settingName, int value) {
- Settings.System.putIntForUser(
- mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
- }
-
- private void setGlobalSetting(String settingName, int value) {
- Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
- }
-
- private boolean waitUntil(Predicate<VibratorService> predicate,
- VibratorService service, long timeout) throws InterruptedException {
- long timeoutTimestamp = SystemClock.uptimeMillis() + timeout;
- boolean predicateResult = false;
- while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) {
- Thread.sleep(10);
- predicateResult = predicate.test(service);
- }
- return predicateResult;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index fdf509504837..a946534f4ecd 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import android.content.Context;
@@ -30,15 +28,12 @@ import android.hardware.power.stats.PowerEntity;
import android.hardware.power.stats.StateResidencyResult;
import android.power.PowerStatsInternal;
import android.util.SparseArray;
-import android.util.SparseLongArray;
import androidx.test.InstrumentationRegistry;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.internal.power.MeasuredEnergyArray;
import org.junit.Before;
-import org.junit.Test;
import java.util.concurrent.CompletableFuture;
@@ -63,44 +58,6 @@ public class BatteryExternalStatsWorkerTest {
mBatteryStatsImpl);
}
- @Test
- public void getEnergyConsumptionData() {
- SparseLongArray expectSubsystems = new SparseLongArray();
- // Add some energy consumers used by BatteryExternalStatsWorker.
- final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
- "display");
- mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
- expectSubsystems.put(MeasuredEnergyArray.SUBSYSTEM_DISPLAY, 12345);
-
- // Add an arbitrary energy consumer unused by BatteryExternalStatsWorker.
- // Must be changed if '154' ever becomes an EnergyConsumerType used by BESW.
- final int someId = mPowerStatsInternal.addEnergyConsumer((byte) 154, 0, "some_consumer");
- mPowerStatsInternal.incrementEnergyConsumption(someId, 34567);
-
- // Inform BESW that PowerStatsInternal is ready to query
- mBatteryExternalStatsWorker.systemServicesReady();
-
- MeasuredEnergyArray energies = mBatteryExternalStatsWorker.getEnergyConsumptionData();
-
- assertEquals(expectSubsystems.size(), energies.size());
- final int size = expectSubsystems.size();
-
- for (int i = 0; i < size; i++) {
- int subsystem = expectSubsystems.keyAt(i);
- // find the subsystem in the returned MeasuredEnergyArray
- int subsystemIndex = -1;
- for (int j = 0; j < size; j++) {
- if (subsystem == energies.getSubsystem(i)) {
- subsystemIndex = i;
- break;
- }
- }
- assertNotEquals("Subsystem " + subsystem + " not found in MeasuredEnergyArray", -1,
- subsystemIndex);
- assertEquals(expectSubsystems.valueAt(i), energies.getEnergy(subsystemIndex));
- }
- }
-
public class TestInjector extends BatteryExternalStatsWorker.Injector {
public TestInjector(Context context) {
super(context);
diff --git a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
index 67d379a47420..1efce39e00fa 100644
--- a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
@@ -16,18 +16,23 @@
package com.android.server.am;
+import static com.android.server.am.MeasuredEnergySnapshot.UNAVAILABLE;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import androidx.test.filters.SmallTest;
-import com.android.internal.power.MeasuredEnergyArray;
+import com.android.server.am.MeasuredEnergySnapshot.MeasuredEnergyDeltaData;
-import org.junit.Before;
import org.junit.Test;
/**
@@ -38,134 +43,198 @@ import org.junit.Test;
*/
@SmallTest
public final class MeasuredEnergySnapshotTest {
- private static final int NUMBER_SUBSYSTEMS = 3;
- private static final int SUBSYSTEM_DISPLAY = 0;
- private static final int SUBSYSTEM_NEVER_USED = 1;
- private static final int SUBSYSTEM_CATAPULT = 2;
-
- private MeasuredEnergySnapshot mSnapshot;
-
- // Basic MeasuredEnergyArray that supports all the subsystems. Out of order on purpose.
- private final int[] mAllSubsystems =
- {SUBSYSTEM_DISPLAY, SUBSYSTEM_CATAPULT, SUBSYSTEM_NEVER_USED};
- // E.g. mAllSubsystems[mSubsystemIndices[SUBSYSTEM_CATAPULT]]=SUBSYSTEM_CATAPULT
- private final int[] mSubsystemIndices = {0, 2, 1};
- private final long[] mCurrentSubsystemEnergyUJ = {111, 0, 0};
- private final MeasuredEnergyArray mOmniEnergyArray = new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- return mAllSubsystems[index];
- }
-
- @Override
- public long getEnergy(int index) {
- return mCurrentSubsystemEnergyUJ[index];
- }
-
- @Override
- public int size() {
- return mAllSubsystems.length;
- }
+ private static final EnergyConsumer CONSUMER_DISPLAY = createEnergyConsumer(
+ 0, 0, EnergyConsumerType.DISPLAY, "Display");
+ private static final EnergyConsumer CONSUMER_OTHER_0 = createEnergyConsumer(
+ 47, 0, EnergyConsumerType.OTHER, "GPU");
+ private static final EnergyConsumer CONSUMER_OTHER_1 = createEnergyConsumer(
+ 1, 1, EnergyConsumerType.OTHER, "HPU");
+ private static final EnergyConsumer CONSUMER_OTHER_2 = createEnergyConsumer(
+ 436, 2, EnergyConsumerType.OTHER, "IPU");
+
+ private static final SparseArray<EnergyConsumer> ALL_ID_CONSUMER_MAP = createIdToConsumerMap(
+ CONSUMER_DISPLAY, CONSUMER_OTHER_0, CONSUMER_OTHER_1, CONSUMER_OTHER_2);
+ private static final SparseArray<EnergyConsumer> SOME_ID_CONSUMER_MAP = createIdToConsumerMap(
+ CONSUMER_DISPLAY);
+
+ // Elements in each results are purposefully out of order.
+ private static final EnergyConsumerResult[] RESULTS_0 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90, new int[] {47, 3}, new long[] {14, 13}),
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 14, null, null),
+ createEnergyConsumerResult(CONSUMER_OTHER_1.id, 0, null, null),
+ // No CONSUMER_OTHER_2
};
- private final MeasuredEnergyArray mJustDisplayEnergyArray = new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- return mAllSubsystems[0];
- }
-
- @Override
- public long getEnergy(int index) {
- return mCurrentSubsystemEnergyUJ[0];
- }
-
- @Override
- public int size() {
- return 1;
- }
+ private static final EnergyConsumerResult[] RESULTS_1 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 24, null, null),
+ createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90, new int[] {47, 3}, new long[] {14, 13}),
+ createEnergyConsumerResult(CONSUMER_OTHER_2.id, 12, new int[] {6}, new long[] {10}),
+ createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000, null, null),
+ };
+ private static final EnergyConsumerResult[] RESULTS_2 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 36, null, null),
+ // No CONSUMER_OTHER_0
+ // No CONSUMER_OTHER_1
+ // No CONSUMER_OTHER_2
+ };
+ private static final EnergyConsumerResult[] RESULTS_3 = new EnergyConsumerResult[] {
+ // No CONSUMER_DISPLAY
+ createEnergyConsumerResult(CONSUMER_OTHER_2.id, 13, new int[] {6}, new long[] {10}),
+ createEnergyConsumerResult(
+ CONSUMER_OTHER_0.id, 190, new int[] {2, 3, 47, 7}, new long[] {9, 18, 14, 6}),
+ createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000, null, null),
+ };
+ private static final EnergyConsumerResult[] RESULTS_4 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 43, null, null),
+ createEnergyConsumerResult(
+ CONSUMER_OTHER_0.id, 290, new int[] {7, 47, 3, 2}, new long[] {6, 14, 18, 11}),
+ // No CONSUMER_OTHER_1
+ createEnergyConsumerResult(CONSUMER_OTHER_2.id, 165, new int[] {6, 47}, new long[] {10, 8}),
};
- @Before
- public void setUp() {
- mSnapshot = new MeasuredEnergySnapshot(NUMBER_SUBSYSTEMS, mOmniEnergyArray);
+ @Test
+ public void testUpdateAndGetDelta_empty() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+ assertNull(snapshot.updateAndGetDelta(null));
+ assertNull(snapshot.updateAndGetDelta(new EnergyConsumerResult[0]));
}
@Test
public void testUpdateAndGetDelta() {
- SparseLongArray result;
-
- // Increment DISPLAY by 15
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 15);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(1, result.size());
- assertEquals(15, result.get(SUBSYSTEM_DISPLAY));
-
- // Increment DISPLAY by 7
- // Increment CATAPULT by 5. But do NOT include (pull) it in the passed in energy array.
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 7);
- incrementEnergyOfSubsystem(SUBSYSTEM_CATAPULT, 5);
- result = mSnapshot.updateAndGetDelta(mJustDisplayEnergyArray); // Just pull display.
- assertEquals(1, result.size());
- assertEquals(7, result.get(SUBSYSTEM_DISPLAY));
-
- // Increment CATAPULT by 64 (in addition to the previous increase of 5)
- incrementEnergyOfSubsystem(SUBSYSTEM_CATAPULT, 64);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(1, result.size());
- assertEquals(5 + 64, result.get(SUBSYSTEM_CATAPULT));
-
- // Do nothing
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals("0 results should not appear at all", 0, result.size());
-
- // Increment DISPLAY by 42
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 42);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(1, result.size());
- assertEquals(42, result.get(SUBSYSTEM_DISPLAY));
-
- // Increment DISPLAY by 106 and CATAPULT by 13
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 106);
- incrementEnergyOfSubsystem(SUBSYSTEM_CATAPULT, 13);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(2, result.size());
- assertEquals(106, result.get(SUBSYSTEM_DISPLAY));
- assertEquals(13, result.get(SUBSYSTEM_CATAPULT));
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+
+ // results0
+ MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0);
+ if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
+ assertEquals(UNAVAILABLE, delta.displayEnergyUJ);
+ assertNull(delta.otherTotalEnergyUJ);
+ assertNull(delta.otherUidEnergiesUJ);
+ }
+
+ // results1
+ delta = snapshot.updateAndGetDelta(RESULTS_1);
+ assertNotNull(delta);
+ assertEquals(24 - 14, delta.displayEnergyUJ);
+
+ assertNotNull(delta.otherTotalEnergyUJ);
+ assertEquals(90 - 90, delta.otherTotalEnergyUJ[0]);
+ assertEquals(12_000 - 0, delta.otherTotalEnergyUJ[1]);
+ assertEquals(0, delta.otherTotalEnergyUJ[2]); // First good pull. Treat delta as 0.
+
+ assertNotNull(delta.otherUidEnergiesUJ);
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[0]); // No change in uid energies
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[1]);
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[2]);
+
+ // results2
+ delta = snapshot.updateAndGetDelta(RESULTS_2);
+ assertNotNull(delta);
+ assertEquals(36 - 24, delta.displayEnergyUJ);
+ assertNull(delta.otherUidEnergiesUJ);
+ assertNull(delta.otherTotalEnergyUJ);
+
+ // results3
+ delta = snapshot.updateAndGetDelta(RESULTS_3);
+ assertNotNull(delta);
+ assertEquals(UNAVAILABLE, delta.displayEnergyUJ);
+
+ assertNotNull(delta.otherTotalEnergyUJ);
+ assertEquals(190 - 90, delta.otherTotalEnergyUJ[0]);
+ assertEquals(12_000 - 12_000, delta.otherTotalEnergyUJ[1]);
+ assertEquals(13 - 12, delta.otherTotalEnergyUJ[2]);
+
+ assertNotNull(delta.otherUidEnergiesUJ);
+ assertEquals(3, delta.otherUidEnergiesUJ[0].size());
+ assertEquals(9 - 0, delta.otherUidEnergiesUJ[0].get(2));
+ assertEquals(18 - 13, delta.otherUidEnergiesUJ[0].get(3));
+ assertEquals(6 - 0, delta.otherUidEnergiesUJ[0].get(7));
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[1]);
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[2]);
+
+ // results4
+ delta = snapshot.updateAndGetDelta(RESULTS_4);
+ assertNotNull(delta);
+ assertEquals(43 - 36, delta.displayEnergyUJ);
+
+ assertNotNull(delta.otherTotalEnergyUJ);
+ assertEquals(290 - 190, delta.otherTotalEnergyUJ[0]);
+ assertEquals(0, delta.otherTotalEnergyUJ[1]); // Not present (e.g. missing data)
+ assertEquals(165 - 13, delta.otherTotalEnergyUJ[2]);
+
+ assertNotNull(delta.otherUidEnergiesUJ);
+ assertEquals(1, delta.otherUidEnergiesUJ[0].size());
+ assertEquals(11 - 9, delta.otherUidEnergiesUJ[0].get(2));
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[1]); // Not present
+ assertEquals(1, delta.otherUidEnergiesUJ[2].size());
+ assertEquals(8, delta.otherUidEnergiesUJ[2].get(47));
}
- private void incrementEnergyOfSubsystem(int subsystem, long energy) {
- mCurrentSubsystemEnergyUJ[mSubsystemIndices[subsystem]] += energy;
+ /** Test updateAndGetDelta() when the results have consumers absent from idToConsumerMap. */
+ @Test
+ public void testUpdateAndGetDelta_some() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP);
+
+ // results0
+ MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0);
+ if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
+ assertEquals(UNAVAILABLE, delta.displayEnergyUJ);
+ assertNull(delta.otherTotalEnergyUJ);
+ assertNull(delta.otherUidEnergiesUJ);
+ }
+
+ // results1
+ delta = snapshot.updateAndGetDelta(RESULTS_1);
+ assertNotNull(delta);
+ assertEquals(24 - 14, delta.displayEnergyUJ);
+ assertNull(delta.otherTotalEnergyUJ); // Although in the results, they're not in the idMap
+ assertNull(delta.otherUidEnergiesUJ);
}
@Test
- public void testUpdateAndGetDelta_null() {
- assertNull(mSnapshot.updateAndGetDelta(null));
+ public void testGetNumOtherOrdinals() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+ assertEquals(3, snapshot.getNumOtherOrdinals());
}
@Test
- public void testHasSubsystem() {
- // Setup MeasuredEnergySnapshot which reported some of the subsystems.
- final int[] subsystems = {SUBSYSTEM_DISPLAY, SUBSYSTEM_CATAPULT};
- MeasuredEnergyArray measuredEnergyArray = new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- return subsystems[index];
- }
+ public void testGetNumOtherOrdinals_none() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP);
+ assertEquals(0, snapshot.getNumOtherOrdinals());
+ }
- @Override
- public long getEnergy(int index) {
- return 0; // Irrelevant for this test.
- }
+ private static EnergyConsumer createEnergyConsumer(int id, int ord, byte type, String name) {
+ final EnergyConsumer ec = new EnergyConsumer();
+ ec.id = id;
+ ec.ordinal = ord;
+ ec.type = type;
+ ec.name = name;
+ return ec;
+ }
- @Override
- public int size() {
- return subsystems.length;
+ private static SparseArray<EnergyConsumer> createIdToConsumerMap(EnergyConsumer ... ecs) {
+ final SparseArray<EnergyConsumer> map = new SparseArray<>();
+ for (EnergyConsumer ec : ecs) {
+ map.put(ec.id, ec);
+ }
+ return map;
+ }
+
+ private static EnergyConsumerResult createEnergyConsumerResult(
+ int id, long energyUWs, int[] uids, long[] uidEnergies) {
+ final EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.id = id;
+ ecr.energyUWs = energyUWs;
+ if (uids != null) {
+ ecr.attribution = new EnergyConsumerAttribution[uids.length];
+ for (int i = 0; i < uids.length; i++) {
+ ecr.attribution[i] = new EnergyConsumerAttribution();
+ ecr.attribution[i].uid = uids[i];
+ ecr.attribution[i].energyUWs = uidEnergies[i];
}
- };
- final MeasuredEnergySnapshot snapshot =
- new MeasuredEnergySnapshot(NUMBER_SUBSYSTEMS, measuredEnergyArray);
+ }
+ return ecr;
+ }
- assertTrue(snapshot.hasSubsystem(SUBSYSTEM_DISPLAY));
- assertTrue(snapshot.hasSubsystem(SUBSYSTEM_CATAPULT));
- assertFalse(snapshot.hasSubsystem(SUBSYSTEM_NEVER_USED));
+ private void assertNullOrEmpty(SparseLongArray a) {
+ if (a != null) assertEquals("Array should be null or empty", 0, a.size());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index b98f0257d7b7..af11fe125a4e 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -30,6 +31,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import com.android.internal.backup.IBackupTransport;
import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
@@ -56,6 +58,7 @@ public class UserBackupManagerServiceTest {
@Mock IBackupObserver mBackupObserver;
@Mock PackageManager mPackageManager;
@Mock TransportClient mTransportClient;
+ @Mock IBackupTransport mBackupTransport;
@Mock BackupEligibilityRules mBackupEligibilityRules;
@@ -132,6 +135,29 @@ public class UserBackupManagerServiceTest {
assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
}
+ @Test
+ public void testGetOperationTypeFromTransport_returnsMigrationForMigrationTransport()
+ throws Exception {
+ when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
+ when(mBackupTransport.getTransportFlags()).thenReturn(
+ BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER);
+
+ int operationType = mService.getOperationTypeFromTransport(mTransportClient);
+
+ assertThat(operationType).isEqualTo(OperationType.MIGRATION);
+ }
+
+ @Test
+ public void testGetOperationTypeFromTransport_returnsBackupByDefault()
+ throws Exception {
+ when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
+ when(mBackupTransport.getTransportFlags()).thenReturn(0);
+
+ int operationType = mService.getOperationTypeFromTransport(mTransportClient);
+
+ assertThat(operationType).isEqualTo(OperationType.BACKUP);
+ }
+
private static PackageInfo getPackageInfo(String packageName) {
PackageInfo packageInfo = new PackageInfo();
packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
new file mode 100644
index 000000000000..b8dfd5672056
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/** Tests for {@link ActiveSourceAction} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class PowerStatusMonitorActionTest {
+
+ private Context mContextSpy;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPhysicalAddress;
+ private HdmiCecLocalDeviceTv mTvDevice;
+
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
+
+ mHdmiControlService = new HdmiControlService(mContextSpy) {
+ @Override
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public void setWiredDeviceConnectionState(
+ int type, int state, String address, String name) {
+ // Do nothing.
+ }
+ };
+ }
+
+ @Override
+ void wakeUp() {
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ protected PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+
+ @Override
+ protected HdmiCecConfig getHdmiCecConfig() {
+ return hdmiCecConfig;
+ }
+ };
+
+ Looper looper = mTestLooper.getLooper();
+ mHdmiControlService.setIoLooper(looper);
+ mNativeWrapper = new FakeNativeWrapper();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(hdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mTvDevice = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mTvDevice.init();
+ mLocalDevices.add(mTvDevice);
+ mTestLooper.dispatchAll();
+ HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[2];
+ hdmiPortInfo[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+ hdmiPortInfo[1] =
+ new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfo);
+ mHdmiControlService.initService();
+ mPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void sourceDevice_1_4_updatesPowerState() {
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_UNKNOWN);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ reportPowerStatus(ADDR_PLAYBACK_1, false, HdmiControlManager.POWER_STATUS_ON);
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON);
+
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(60));
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ reportPowerStatus(ADDR_PLAYBACK_1, false, HdmiControlManager.POWER_STATUS_STANDBY);
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_STANDBY);
+ }
+
+ private void assertPowerStatus(int logicalAddress, int powerStatus) {
+ HdmiDeviceInfo deviceInfo = mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(
+ logicalAddress);
+ assertThat(deviceInfo).isNotNull();
+ assertThat(deviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
+ }
+
+ @Test
+ public void sourceDevice_2_0_doesNotUpdatePowerState() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+ reportPowerStatus(ADDR_PLAYBACK_1, true, HdmiControlManager.POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(60));
+ mTestLooper.dispatchAll();
+
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON);
+ }
+
+ @Test
+ public void mixedSourceDevices_localDevice_1_4_updatesAll() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ mTestLooper.dispatchAll();
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_2, 0x2000);
+ reportPowerStatus(ADDR_PLAYBACK_2, true, HdmiControlManager.POWER_STATUS_ON);
+
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_UNKNOWN);
+ assertPowerStatus(ADDR_PLAYBACK_2, HdmiControlManager.POWER_STATUS_ON);
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_2);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus2);
+ }
+
+ @Test
+ public void mixedSourceDevices_localDevice_2_0_onlyUpdates_1_4() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mTestLooper.dispatchAll();
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_2, 0x2000);
+ reportPowerStatus(ADDR_PLAYBACK_2, true, HdmiControlManager.POWER_STATUS_ON);
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_2);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus2);
+ }
+
+ private void sendMessageFromPlaybackDevice(int logicalAddress, int physicalAddress) {
+ HdmiCecMessage playbackDevice = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ logicalAddress, physicalAddress, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mNativeWrapper.onCecMessage(playbackDevice);
+ mTestLooper.dispatchAll();
+ }
+
+ private void reportPowerStatus(int logicalAddress, boolean broadcast, int powerStatus) {
+ int destination = broadcast ? ADDR_BROADCAST : ADDR_TV;
+ HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+ logicalAddress, destination,
+ powerStatus);
+ mNativeWrapper.onCecMessage(reportPowerStatus);
+ mTestLooper.dispatchAll();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 22020ad45666..bc84e350a329 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -96,7 +96,7 @@ public class DexoptOptionsTests {
int[] reasons = new int[] {
PackageManagerService.REASON_FIRST_BOOT,
- PackageManagerService.REASON_BOOT,
+ PackageManagerService.REASON_POST_BOOT,
PackageManagerService.REASON_INSTALL,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
PackageManagerService.REASON_AB_OTA,
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index b28994cafa0f..55748366c1ba 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index 6dcfb42f7f08..daa1b25de22f 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -27,7 +27,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index b4fd3024a634..781cfec77f08 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1834,7 +1834,7 @@ public class DisplayContentTests extends WindowTestsBase {
mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
- private void performLayout(DisplayContent dc) {
+ static void performLayout(DisplayContent dc) {
dc.setLayoutNeeded();
dc.performLayout(true /* initial */, false /* updateImeWindows */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 3231f8b6551a..896969548af3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -67,11 +67,14 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.when;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.view.Gravity;
import android.view.InputWindowHandle;
import android.view.InsetsState;
import android.view.SurfaceControl;
@@ -559,6 +562,46 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(window.isVisibleByPolicy());
}
+ @Test
+ public void testCompatOverrideScale() {
+ final float overrideScale = 2; // 0.5x on client side.
+ final CompatModePackages cmp = mWm.mAtmService.mCompatModePackages;
+ spyOn(cmp);
+ doReturn(overrideScale).when(cmp).getCompatScale(anyString(), anyInt());
+ final WindowState w = createWindow(null, TYPE_APPLICATION_OVERLAY, "win");
+ makeWindowVisible(w);
+ w.setRequestedSize(100, 200);
+ w.mAttrs.width = w.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ w.mAttrs.gravity = Gravity.TOP | Gravity.LEFT;
+ DisplayContentTests.performLayout(mDisplayContent);
+
+ // Frame on screen = 100x200. Compat frame on client = 50x100.
+ final Rect unscaledCompatFrame = new Rect(w.getWindowFrames().mCompatFrame);
+ unscaledCompatFrame.scale(overrideScale);
+ assertEquals(w.getWindowFrames().mFrame, unscaledCompatFrame);
+
+ // Surface should apply the scale.
+ w.prepareSurfaces();
+ verify(w.getPendingTransaction()).setMatrix(w.getSurfaceControl(),
+ overrideScale, 0, 0, overrideScale);
+
+ // According to "dp * density / 160 = px", density is scaled and the size in dp is the same.
+ final CompatibilityInfo compatInfo = cmp.compatibilityInfoForPackageLocked(
+ mContext.getApplicationInfo());
+ final Configuration winConfig = w.getConfiguration();
+ final Configuration clientConfig = new Configuration(w.getConfiguration());
+ compatInfo.applyToConfiguration(clientConfig.densityDpi, clientConfig);
+
+ assertEquals(winConfig.screenWidthDp, clientConfig.screenWidthDp);
+ assertEquals(winConfig.screenHeightDp, clientConfig.screenHeightDp);
+ assertEquals(winConfig.smallestScreenWidthDp, clientConfig.smallestScreenWidthDp);
+ assertEquals(winConfig.densityDpi, (int) (clientConfig.densityDpi * overrideScale));
+
+ final Rect unscaledClientBounds = new Rect(clientConfig.windowConfiguration.getBounds());
+ unscaledClientBounds.scale(overrideScale);
+ assertEquals(w.getWindowConfiguration().getBounds(), unscaledClientBounds);
+ }
+
@UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE })
@Test
public void testRequestDrawIfNeeded() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index eb6c6ed349de..83b30a9747d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -337,6 +337,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
attrs.setTitle(name);
+ attrs.packageName = "test";
final WindowState w = new WindowState(service, session, iWindow, token, parent,
OP_NONE, attrs, VISIBLE, ownerId, userId,
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 3b06fd3d4ea1..170ed3eff614 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2024,7 +2024,7 @@ public abstract class ConnectionService extends Service {
boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean(
- PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false);
+ PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true);
Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, "
+ "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, "
+ " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming,
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 99f2e5ee0755..c6757fba4d53 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -646,7 +646,7 @@ public class Annotation {
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE})
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED})
public @interface OverrideNetworkType {}
/**
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 1fcb504e7895..5b5570b27a9b 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -66,9 +66,26 @@ public final class TelephonyDisplayInfo implements Parcelable {
* {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
* capability or is currently connected to the secondary
* {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands.
+ * @deprecated Use{@link #OVERRIDE_NETWORK_TYPE_NR_ADVANCED} instead.
*/
+ @Deprecated
public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4;
+ /**
+ * Override network type when the device is connected NR cellular network and the data rate is
+ * higher than the generic 5G date rate.
+ * Including but not limited to
+ * <ul>
+ * <li>The device is connected to the NR cellular network on millimeter wave bands. </li>
+ * <li>The device is connected to the specific network which the carrier is using
+ * proprietary means to provide a faster overall data connection than would be otherwise
+ * possible. This may include using other bands unique to the carrier, or carrier
+ * aggregation, for example.</li>
+ * </ul>
+ * One of the use case is that UX can show a different icon, for example, "5G+"
+ */
+ public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 4;
+
@NetworkType
private final int mNetworkType;
@@ -169,7 +186,7 @@ public final class TelephonyDisplayInfo implements Parcelable {
case OVERRIDE_NETWORK_TYPE_LTE_CA: return "LTE_CA";
case OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return "LTE_ADV_PRO";
case OVERRIDE_NETWORK_TYPE_NR_NSA: return "NR_NSA";
- case OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: return "NR_NSA_MMWAVE";
+ case OVERRIDE_NETWORK_TYPE_NR_ADVANCED: return "NR_NSA_MMWAVE";
default: return "UNKNOWN";
}
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index d1d6a26790fd..0f920b36cffd 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -16,8 +16,11 @@
package com.android.framework.permission.tests;
+import android.content.Context;
import android.os.Binder;
-import android.os.IVibratorService;
+import android.os.CombinedVibrationEffect;
+import android.os.IBinder;
+import android.os.IVibratorManagerService;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -32,27 +35,28 @@ import junit.framework.TestCase;
* Verify that Hardware apis cannot be called without required permissions.
*/
@SmallTest
-public class VibratorServicePermissionTest extends TestCase {
+public class VibratorManagerServicePermissionTest extends TestCase {
- private IVibratorService mVibratorService;
+ private IVibratorManagerService mVibratorService;
@Override
protected void setUp() throws Exception {
- mVibratorService = IVibratorService.Stub.asInterface(
- ServiceManager.getService("vibrator"));
+ mVibratorService = IVibratorManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
}
/**
- * Test that calling {@link android.os.IVibratorService#vibrate(long)} requires permissions.
+ * Test that calling {@link android.os.IVibratorManagerService#vibrate(int, String,
+ * CombinedVibrationEffect, VibrationAttributes, String, IBinder)} requires permissions.
* <p>Tests permission:
- * {@link android.Manifest.permission#VIBRATE}
- * @throws RemoteException
+ * {@link android.Manifest.permission#VIBRATE}
*/
public void testVibrate() throws RemoteException {
try {
- final VibrationEffect effect =
- VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
- final VibrationAttributes attrs = new VibrationAttributes.Builder()
+ CombinedVibrationEffect effect =
+ CombinedVibrationEffect.createSynced(
+ VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
+ VibrationAttributes attrs = new VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_ALARM)
.build();
mVibratorService.vibrate(Process.myUid(), null, effect, attrs,
@@ -64,10 +68,10 @@ public class VibratorServicePermissionTest extends TestCase {
}
/**
- * Test that calling {@link android.os.IVibratorService#cancelVibrate()} requires permissions.
+ * Test that calling {@link android.os.IVibratorManagerService#cancelVibrate(IBinder)} requires
+ * permissions.
* <p>Tests permission:
- * {@link android.Manifest.permission#VIBRATE}
- * @throws RemoteException
+ * {@link android.Manifest.permission#VIBRATE}
*/
public void testCancelVibrate() throws RemoteException {
try {